diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8a9b2b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/util-linux-2.23.2.tar.xz diff --git a/.util-linux.metadata b/.util-linux.metadata new file mode 100644 index 0000000..0cacf1c --- /dev/null +++ b/.util-linux.metadata @@ -0,0 +1 @@ +1c3023304dd66663fd314bee424d7cc829fff538 SOURCES/util-linux-2.23.2.tar.xz diff --git a/SOURCES/0060-build-sys-add-CFLAGS-and-LDFLAGS-for-daemons-and-sha.patch b/SOURCES/0060-build-sys-add-CFLAGS-and-LDFLAGS-for-daemons-and-sha.patch new file mode 100644 index 0000000..cc9d680 --- /dev/null +++ b/SOURCES/0060-build-sys-add-CFLAGS-and-LDFLAGS-for-daemons-and-sha.patch @@ -0,0 +1,166 @@ +From 097c66ef2dd4779e01d13ad4a5f9733334fa9d1a Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 27 Aug 2013 10:02:04 +0200 +Subject: [PATCH 60/84] build-sys: add CFLAGS and LDFLAGS for daemons and + shared libs + +This is necessary for paranoid security guys who believe that things +like "-Wl,-z,relro" or "-Wl,-z,bind_now" is a way how to make the +world a safer place... + +[rhel7: add also SOLIB_* to libfdisk] + +Upstream: http://github.com/karelzak/util-linux/commit/03d00d495f3b505d9cae967c629fb38aca301e01 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1092520 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + Documentation/howto-compilation.txt | 10 +++++----- + configure.ac | 10 ++++++++++ + libblkid/src/Makemodule.am | 2 ++ + libfdisk/src/Makemodule.am | 3 +++ + libmount/src/Makemodule.am | 2 ++ + libuuid/src/Makemodule.am | 7 ++++++- + misc-utils/Makemodule.am | 3 ++- + 7 files changed, 30 insertions(+), 7 deletions(-) + +diff --git a/Documentation/howto-compilation.txt b/Documentation/howto-compilation.txt +index bebe0d2..4b39246 100644 +--- a/Documentation/howto-compilation.txt ++++ b/Documentation/howto-compilation.txt +@@ -27,12 +27,12 @@ Compiling + The SUID_* feature is currently supported for chfn, chsh, + newgrp, su, write, mount, and umount. + +- Preferred compilation options for developers, when +- using gcc, are: ++ Use DAEMON_CFLAGS and DAEMON_LDFLAGS when you want to define ++ special compiler options for daemons; supported for uuidd. + +- export CFLAGS="-Wmissing-parameter-type -Wsign-compare +- -Wtype-limits -Wuninitialized -Wunused-parameter +- -Wunused-but-set-parameter -fno-common" ++ Use SOLIB_CFLAGS and SOLIB_LDFLAGS when you want to define ++ special compiler options for shared libraries; supported for ++ libmount, libblkid and libuuid. + + FIXME: add notes about klib and uClib. + +diff --git a/configure.ac b/configure.ac +index f7c27cd..f3c7214 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1538,6 +1538,16 @@ AC_ARG_VAR([SUID_CFLAGS], + AC_ARG_VAR([SUID_LDFLAGS], + [LDFLAGS used for binaries which are usually with the suid bit]) + ++AC_ARG_VAR([DAEMON_CFLAGS], ++ [CFLAGS used for binaries which are usually executed as daemons]) ++AC_ARG_VAR([DAEMON_LDFLAGS], ++ [LDFLAGS used for binaries which are usually executed as daemons]) ++ ++AC_ARG_VAR([SOLIB_CFLAGS], ++ [CFLAGS used for shared libraries]) ++AC_ARG_VAR([SOLIB_LDFLAGS], ++ [LDFLAGS used for shared libraries]) ++ + LIBS="" + + +diff --git a/libblkid/src/Makemodule.am b/libblkid/src/Makemodule.am +index 04d8621..1563976 100644 +--- a/libblkid/src/Makemodule.am ++++ b/libblkid/src/Makemodule.am +@@ -113,6 +113,7 @@ libblkid_la_LIBADD = libcommon.la + + + libblkid_la_CFLAGS = \ ++ $(SOLIB_CFLAGS) \ + -I$(ul_libblkid_incdir) \ + -I$(top_srcdir)/libblkid/src + +@@ -128,6 +129,7 @@ libblkid_la_DEPENDENCIES = \ + libblkid/src/blkid.h.in + + libblkid_la_LDFLAGS = \ ++ $(SOLIB_LDFLAGS) \ + -Wl,--version-script=$(top_srcdir)/libblkid/src/blkid.sym \ + -version-info $(LIBBLKID_VERSION_INFO) + +diff --git a/libfdisk/src/Makemodule.am b/libfdisk/src/Makemodule.am +index fbfb1b4..5c50001 100644 +--- a/libfdisk/src/Makemodule.am ++++ b/libfdisk/src/Makemodule.am +@@ -25,6 +25,8 @@ nodist_libfdisk_la_SOURCES = libfdisk/src/fdiskP.h + libfdisk_la_LIBADD = libcommon.la + + libfdisk_la_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(SOLIB_CFLAGS) \ + -I$(ul_libfdisk_incdir) \ + -I$(top_srcdir)/libfdisk/src + +@@ -39,6 +41,7 @@ libfdisk_la_CFLAGS += -I$(ul_libuuid_incdir) + endif + + libfdisk_la_DEPENDENCIES = $(libfdisk_la_LIBADD) ++libfdisk_la_LDFLAGS = $(SOLIB_LDFLAGS) + + + check_PROGRAMS += \ +diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am +index 494e02a..8ef07e5 100644 +--- a/libmount/src/Makemodule.am ++++ b/libmount/src/Makemodule.am +@@ -33,6 +33,7 @@ nodist_libmount_la_SOURCES = libmount/src/mountP.h + libmount_la_LIBADD = libcommon.la libblkid.la $(SELINUX_LIBS) + + libmount_la_CFLAGS = \ ++ $(SOLIB_CFLAGS) \ + -I$(ul_libblkid_incdir) \ + -I$(ul_libmount_incdir) \ + -I$(top_srcdir)/libmount/src +@@ -43,6 +44,7 @@ libmount_la_DEPENDENCIES = \ + libmount/src/libmount.h.in + + libmount_la_LDFLAGS = \ ++ $(SOLIB_LDFLAGS) \ + -Wl,--version-script=$(top_srcdir)/libmount/src/libmount.sym \ + -version-info $(LIBMOUNT_VERSION_INFO) + +diff --git a/libuuid/src/Makemodule.am b/libuuid/src/Makemodule.am +index 73f1ba9..a20cb4c 100644 +--- a/libuuid/src/Makemodule.am ++++ b/libuuid/src/Makemodule.am +@@ -29,9 +29,14 @@ libuuid_la_SOURCES = \ + + libuuid_la_DEPENDENCIES = libuuid/src/uuid.sym + libuuid_la_LIBADD = $(SOCKET_LIBS) +-libuuid_la_CFLAGS = -I$(ul_libuuid_incdir) -Ilibuuid/src ++ ++libuuid_la_CFLAGS = \ ++ $(SOLIB_CFLAGS) \ ++ -I$(ul_libuuid_incdir) \ ++ -Ilibuuid/src + + libuuid_la_LDFLAGS = \ ++ $(SOLIB_LDFLAGS) \ + -Wl,--version-script=$(top_srcdir)/libuuid/src/uuid.sym \ + -version-info $(LIBUUID_VERSION_INFO) + +diff --git a/misc-utils/Makemodule.am b/misc-utils/Makemodule.am +index a615047..70a78f2 100644 +--- a/misc-utils/Makemodule.am ++++ b/misc-utils/Makemodule.am +@@ -77,7 +77,8 @@ if BUILD_UUIDD + usrsbin_exec_PROGRAMS += uuidd + dist_man_MANS += misc-utils/uuidd.8 + uuidd_LDADD = $(LDADD) libuuid.la +-uuidd_CFLAGS = $(AM_CFLAGS) -I$(ul_libuuid_incdir) ++uuidd_CFLAGS = $(DAEMON_CFLAGS) $(AM_CFLAGS) -I$(ul_libuuid_incdir) ++uuidd_LDFLAGS = $(DAEMON_LDFLAGS) $(AM_LDFLAGS) + uuidd_SOURCES = misc-utils/uuidd.c + if USE_SOCKET_ACTIVATION + uuidd_SOURCES += misc-utils/sd-daemon.c misc-utils/sd-daemon.h +-- +2.7.4 + diff --git a/SOURCES/0061-libmount-be-more-restrictive-about-valid-tag-names.patch b/SOURCES/0061-libmount-be-more-restrictive-about-valid-tag-names.patch new file mode 100644 index 0000000..7e5d394 --- /dev/null +++ b/SOURCES/0061-libmount-be-more-restrictive-about-valid-tag-names.patch @@ -0,0 +1,183 @@ +From f5ef29a5b5c51fe2039352dabcc4946fa2f55861 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 2 Jul 2013 10:46:10 +0200 +Subject: [PATCH 61/84] libmount: be more restrictive about valid tag names + + # mount DUMMY=filename.img /mnt + +The 'DUMMY=filename.img' is a filename and should not be +interpreted as tag name. The valid tag names are LABEL, UUID, +PARTLABEL and PARTUUID only. + +Upstream: http://github.com/karelzak/util-linux/commit/2c6b25f01802808b142d450af3352605720899da +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1248003 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libmount/src/cache.c | 29 ++++++++++++----------------- + libmount/src/fs.c | 9 ++++++--- + libmount/src/mountP.h | 2 ++ + libmount/src/tab.c | 20 ++++++++------------ + libmount/src/utils.c | 12 ++++++++++++ + 5 files changed, 40 insertions(+), 32 deletions(-) + +diff --git a/libmount/src/cache.c b/libmount/src/cache.c +index 7b65122..43a4daf 100644 +--- a/libmount/src/cache.c ++++ b/libmount/src/cache.c +@@ -583,22 +583,18 @@ error: + char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache) + { + char *cn = NULL; ++ char *t = NULL, *v = NULL; + + if (!spec) + return NULL; + +- if (strchr(spec, '=')) { +- char *tag, *val; +- +- if (!blkid_parse_tag_string(spec, &tag, &val)) { +- cn = mnt_resolve_tag(tag, val, cache); +- +- free(tag); +- free(val); +- } +- } else ++ if (blkid_parse_tag_string(spec, &t, &v) == 0 && mnt_valid_tagname(t)) ++ cn = mnt_resolve_tag(t, v, cache); ++ else + cn = mnt_resolve_path(spec, cache); + ++ free(t); ++ free(v); + return cn; + } + +@@ -663,6 +659,7 @@ int test_read_tags(struct libmnt_test *ts, int argc, char *argv[]) + + while(fgets(line, sizeof(line), stdin)) { + size_t sz = strlen(line); ++ char *t = NULL, *v = NULL; + + if (sz > 0 && line[sz - 1] == '\n') + line[sz - 1] = '\0'; +@@ -674,16 +671,14 @@ int test_read_tags(struct libmnt_test *ts, int argc, char *argv[]) + if (mnt_cache_read_tags(cache, line) < 0) + fprintf(stderr, "%s: read tags failed\n", line); + +- } else if (strchr(line, '=')) { +- char *tag, *val; ++ } else if (blkid_parse_tag_string(line, &t, &v) == 0) { + const char *cn = NULL; + +- if (!blkid_parse_tag_string(line, &tag, &val)) { +- cn = cache_find_tag(cache, tag, val); ++ if (mnt_valid_tagname(t)) ++ cn = cache_find_tag(cache, t, v); ++ free(t); ++ free(v); + +- free(tag); +- free(val); +- } + if (cn) + printf("%s: %s\n", line, cn); + else +diff --git a/libmount/src/fs.c b/libmount/src/fs.c +index c95cdc7..75e3bbb 100644 +--- a/libmount/src/fs.c ++++ b/libmount/src/fs.c +@@ -318,9 +318,12 @@ int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source) + + assert(fs); + +- if (source && *source != '/' && strchr(source, '=')) { +- if (blkid_parse_tag_string(source, &t, &v) != 0) +- return -1; ++ if (source && blkid_parse_tag_string(source, &t, &v) == 0 && ++ !mnt_valid_tagname(t)) { ++ /* parsable but unknown tag -- ignore */ ++ free(t); ++ free(v); ++ t = v = NULL; + } + + if (fs->source != source) +diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h +index e064a68..7b0848f 100644 +--- a/libmount/src/mountP.h ++++ b/libmount/src/mountP.h +@@ -136,6 +136,8 @@ extern int startswith(const char *s, const char *sx) + + extern char *stripoff_last_component(char *path); + ++extern int mnt_valid_tagname(const char *tagname); ++ + extern int is_file_empty(const char *name); + + extern int mkdir_p(const char *path, mode_t mode); +diff --git a/libmount/src/tab.c b/libmount/src/tab.c +index e3524a8..1ba1eec 100644 +--- a/libmount/src/tab.c ++++ b/libmount/src/tab.c +@@ -801,7 +801,8 @@ struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag, + struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb, + const char *source, int direction) + { +- struct libmnt_fs *fs = NULL; ++ struct libmnt_fs *fs; ++ char *t = NULL, *v = NULL; + + assert(tb); + +@@ -812,18 +813,13 @@ struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb, + + DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: '%s'", source)); + +- if (source && *source && strchr(source, '=')) { +- char *tag, *val; +- +- if (blkid_parse_tag_string(source, &tag, &val) == 0) { +- +- fs = mnt_table_find_tag(tb, tag, val, direction); +- +- free(tag); +- free(val); +- } +- } else ++ if (blkid_parse_tag_string(source, &t, &v) || !mnt_valid_tagname(t)) + fs = mnt_table_find_srcpath(tb, source, direction); ++ else ++ fs = mnt_table_find_tag(tb, t, v, direction); ++ ++ free(t); ++ free(v); + + return fs; + } +diff --git a/libmount/src/utils.c b/libmount/src/utils.c +index 6a444ad..9305bb8 100644 +--- a/libmount/src/utils.c ++++ b/libmount/src/utils.c +@@ -65,6 +65,18 @@ int is_file_empty(const char *name) + return (stat(name, &st) != 0 || st.st_size == 0); + } + ++int mnt_valid_tagname(const char *tagname) ++{ ++ if (tagname && *tagname && ( ++ strcmp("UUID", tagname) == 0 || ++ strcmp("LABEL", tagname) == 0 || ++ strcmp("PARTUUID", tagname) == 0 || ++ strcmp("PARTLABEL", tagname) == 0)) ++ return 1; ++ ++ return 0; ++} ++ + int mnt_parse_offset(const char *str, size_t len, uintmax_t *res) + { + char *p; +-- +2.7.4 + diff --git a/SOURCES/0062-mount-umount-swapon-fsck-lsblk-findmnt-ignore-malfor.patch b/SOURCES/0062-mount-umount-swapon-fsck-lsblk-findmnt-ignore-malfor.patch new file mode 100644 index 0000000..5bfb445 --- /dev/null +++ b/SOURCES/0062-mount-umount-swapon-fsck-lsblk-findmnt-ignore-malfor.patch @@ -0,0 +1,172 @@ +From 28663e752e125da99f8636ea0227d168f1e0e6aa Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 15 Oct 2015 11:53:44 +0200 +Subject: [PATCH 62/84] mount, umount, swapon, fsck, lsblk, findmnt: ignore + malformed lines + +The libmount provides way how to deal with parsing errors in fstab -- +on error callback function is executed and according to the return +libmount manipulate with the malformed line, possible are three +states: + + 1/ fatal error; all file ignored (callback rc < 0) + 2/ recoverable error; malformed line ignored (callback rc > 0) + 3/ ignore the error (callback rc == 0) + +The 2/ is the default if no callback specified. + +Unfortunately our utils uses 3/. The correct way is to use 2/. + +Upstream: http://github.com/karelzak/util-linux/commit/1cd9d0d7463850ef6b16a78b8a55e56dbf9a8db1 +Upstream: http://github.com/karelzak/util-linux/commit/1bb02a2da9f1bf7d80b352d540b29371099ab570 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1271850 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + disk-utils/fsck.c | 2 +- + libmount/src/tab_parse.c | 2 +- + misc-utils/findmnt.c | 2 +- + misc-utils/lsblk.c | 11 +++++++++++ + sys-utils/mount.c | 2 +- + sys-utils/swapon-common.c | 11 +++++++++++ + sys-utils/umount.c | 2 +- + 7 files changed, 27 insertions(+), 5 deletions(-) + +diff --git a/disk-utils/fsck.c b/disk-utils/fsck.c +index 6e3a2c0..3ef8e5b 100644 +--- a/disk-utils/fsck.c ++++ b/disk-utils/fsck.c +@@ -421,7 +421,7 @@ static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)), + const char *filename, int line) + { + warnx(_("%s: parse error at line %d -- ignore"), filename, line); +- return 0; ++ return 1; + } + + /* +diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c +index e930fd8..987e671 100644 +--- a/libmount/src/tab_parse.c ++++ b/libmount/src/tab_parse.c +@@ -540,7 +540,7 @@ int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filenam + } + if (rc) { + mnt_free_fs(fs); +- if (rc == 1) ++ if (rc > 0) + continue; /* recoverable error */ + if (feof(f)) + break; +diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c +index 615ba08..f16da91 100644 +--- a/misc-utils/findmnt.c ++++ b/misc-utils/findmnt.c +@@ -752,7 +752,7 @@ static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)), + const char *filename, int line) + { + warnx(_("%s: parse error at line %d"), filename, line); +- return 0; ++ return 1; + } + + static char **append_tabfile(char **files, int *nfiles, char *filename) +diff --git a/misc-utils/lsblk.c b/misc-utils/lsblk.c +index 9e12a90..cd28c1d 100644 +--- a/misc-utils/lsblk.c ++++ b/misc-utils/lsblk.c +@@ -337,6 +337,15 @@ static char *get_device_path(struct blkdev_cxt *cxt) + return xstrdup(path); + } + ++static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)), ++ const char *filename, int line) ++{ ++ if (filename) ++ warnx(_("%s: parse error: ignore entry at line %d."), ++ filename, line); ++ return 1; ++} ++ + static int is_active_swap(const char *filename) + { + if (!swaps) { +@@ -346,6 +355,7 @@ static int is_active_swap(const char *filename) + if (!mntcache) + mntcache = mnt_new_cache(); + ++ mnt_table_set_parser_errcb(swaps, table_parser_errcb); + mnt_table_set_cache(swaps, mntcache); + mnt_table_parse_swaps(swaps, NULL); + } +@@ -368,6 +378,7 @@ static char *get_device_mountpoint(struct blkdev_cxt *cxt) + if (!mntcache) + mntcache = mnt_new_cache(); + ++ mnt_table_set_parser_errcb(mtab, table_parser_errcb); + mnt_table_set_cache(mtab, mntcache); + mnt_table_parse_mtab(mtab, NULL); + } +diff --git a/sys-utils/mount.c b/sys-utils/mount.c +index 0998b01..f332070 100644 +--- a/sys-utils/mount.c ++++ b/sys-utils/mount.c +@@ -101,7 +101,7 @@ static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__) + if (filename) + warnx(_("%s: parse error: ignore entry at line %d."), + filename, line); +- return 0; ++ return 1; + } + + /* +diff --git a/sys-utils/swapon-common.c b/sys-utils/swapon-common.c +index 5c95ef3..5f14ddb 100644 +--- a/sys-utils/swapon-common.c ++++ b/sys-utils/swapon-common.c +@@ -11,12 +11,22 @@ static struct libmnt_table *swaps, *fstab; + + struct libmnt_cache *mntcache; + ++static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)), ++ const char *filename, int line) ++{ ++ if (filename) ++ warnx(_("%s: parse error: ignore entry at line %d."), ++ filename, line); ++ return 1; ++} ++ + struct libmnt_table *get_fstab(void) + { + if (!fstab) { + fstab = mnt_new_table(); + if (!fstab) + return NULL; ++ mnt_table_set_parser_errcb(fstab, table_parser_errcb); + mnt_table_set_cache(fstab, mntcache); + if (mnt_table_parse_fstab(fstab, NULL) != 0) + return NULL; +@@ -32,6 +42,7 @@ struct libmnt_table *get_swaps(void) + if (!swaps) + return NULL; + mnt_table_set_cache(swaps, mntcache); ++ mnt_table_set_parser_errcb(swaps, table_parser_errcb); + if (mnt_table_parse_swaps(swaps, NULL) != 0) + return NULL; + } +diff --git a/sys-utils/umount.c b/sys-utils/umount.c +index 1bd275f..9c47744 100644 +--- a/sys-utils/umount.c ++++ b/sys-utils/umount.c +@@ -45,7 +45,7 @@ static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__) + if (filename) + warnx(_("%s: parse error: ignore entry at line %d."), + filename, line); +- return 0; ++ return 1; + } + + +-- +2.7.4 + diff --git a/SOURCES/0063-login-mount-fix-__SC_GETPW_R_SIZE_MAX-usage.patch b/SOURCES/0063-login-mount-fix-__SC_GETPW_R_SIZE_MAX-usage.patch new file mode 100644 index 0000000..73f80e0 --- /dev/null +++ b/SOURCES/0063-login-mount-fix-__SC_GETPW_R_SIZE_MAX-usage.patch @@ -0,0 +1,146 @@ +From 3d1333293ef48117060cd4e285e9c49a6d061e83 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 15 Dec 2015 12:25:56 +0100 +Subject: [PATCH 63/84] login, mount: fix __SC_GETPW_R_SIZE_MAX usage + +sysconf(_SC_GETPW_R_SIZE_MAX) returns initial suggested size for pwd +buffer (see getpwnam_r man page or POSIX). This is not large enough in +some cases. + +Yes, this sysconf option is misnamed (should be _SC_GETPW_R_SIZE_MIN). + +Upstream: http://github.com/karelzak/util-linux/commit/f7ac9e71b18fa7314151f2ab65ee0bdd2ea89c07 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1290689 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + include/c.h | 7 +++++++ + libmount/src/utils.c | 25 ++++++------------------- + login-utils/login.c | 12 ++---------- + 3 files changed, 15 insertions(+), 29 deletions(-) + +diff --git a/include/c.h b/include/c.h +index a50e8a5..7b59ce8 100644 +--- a/include/c.h ++++ b/include/c.h +@@ -300,4 +300,11 @@ static inline int usleep(useconds_t usec) + # define SEEK_HOLE 4 + #endif + ++/* ++ * Note that sysconf(_SC_GETPW_R_SIZE_MAX) returns *initial* suggested size for ++ * pwd buffer and in some cases it is not large enough. See POSIX and ++ * getpwnam_r man page for more details. ++ */ ++#define UL_GETPW_BUFSIZ (16 * 1024) ++ + #endif /* UTIL_LINUX_C_H */ +diff --git a/libmount/src/utils.c b/libmount/src/utils.c +index 9305bb8..7c6f5b1 100644 +--- a/libmount/src/utils.c ++++ b/libmount/src/utils.c +@@ -538,16 +538,6 @@ int mnt_get_filesystems(char ***filesystems, const char *pattern) + return rc; + } + +-static size_t get_pw_record_size(void) +-{ +-#ifdef _SC_GETPW_R_SIZE_MAX +- long sz = sysconf(_SC_GETPW_R_SIZE_MAX); +- if (sz > 0) +- return sz; +-#endif +- return 16384; +-} +- + /* + * Returns allocated string with username or NULL. + */ +@@ -555,14 +545,13 @@ char *mnt_get_username(const uid_t uid) + { + struct passwd pwd; + struct passwd *res; +- size_t sz = get_pw_record_size(); + char *buf, *username = NULL; + +- buf = malloc(sz); ++ buf = malloc(UL_GETPW_BUFSIZ); + if (!buf) + return NULL; + +- if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res) ++ if (!getpwuid_r(uid, &pwd, buf, UL_GETPW_BUFSIZ, &res) && res) + username = strdup(pwd.pw_name); + + free(buf); +@@ -574,17 +563,16 @@ int mnt_get_uid(const char *username, uid_t *uid) + int rc = -1; + struct passwd pwd; + struct passwd *pw; +- size_t sz = get_pw_record_size(); + char *buf; + + if (!username || !uid) + return -EINVAL; + +- buf = malloc(sz); ++ buf = malloc(UL_GETPW_BUFSIZ); + if (!buf) + return -ENOMEM; + +- if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) { ++ if (!getpwnam_r(username, &pwd, buf, UL_GETPW_BUFSIZ, &pw) && pw) { + *uid= pw->pw_uid; + rc = 0; + } else { +@@ -602,17 +590,16 @@ int mnt_get_gid(const char *groupname, gid_t *gid) + int rc = -1; + struct group grp; + struct group *gr; +- size_t sz = get_pw_record_size(); + char *buf; + + if (!groupname || !gid) + return -EINVAL; + +- buf = malloc(sz); ++ buf = malloc(UL_GETPW_BUFSIZ); + if (!buf) + return -ENOMEM; + +- if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) { ++ if (!getgrnam_r(groupname, &grp, buf, UL_GETPW_BUFSIZ, &gr) && gr) { + *gid= gr->gr_gid; + rc = 0; + } else { +diff --git a/login-utils/login.c b/login-utils/login.c +index a59dd3a..e0e960f 100644 +--- a/login-utils/login.c ++++ b/login-utils/login.c +@@ -671,22 +671,14 @@ static struct passwd *get_passwd_entry(const char *username, + struct passwd *pwd) + { + struct passwd *res = NULL; +- size_t sz = 16384; + int x; + + if (!pwdbuf || !username) + return NULL; + +-#ifdef _SC_GETPW_R_SIZE_MAX +- { +- long xsz = sysconf(_SC_GETPW_R_SIZE_MAX); +- if (xsz > 0) +- sz = (size_t) xsz; +- } +-#endif +- *pwdbuf = xrealloc(*pwdbuf, sz); ++ *pwdbuf = xrealloc(*pwdbuf, UL_GETPW_BUFSIZ); + +- x = getpwnam_r(username, pwd, *pwdbuf, sz, &res); ++ x = getpwnam_r(username, pwd, *pwdbuf, UL_GETPW_BUFSIZ, &res); + if (!res) { + errno = x; + return NULL; +-- +2.7.4 + diff --git a/SOURCES/0064-bash-completion-use-n-as-IFS-when-ask-for-filenames.patch b/SOURCES/0064-bash-completion-use-n-as-IFS-when-ask-for-filenames.patch new file mode 100644 index 0000000..6e63208 --- /dev/null +++ b/SOURCES/0064-bash-completion-use-n-as-IFS-when-ask-for-filenames.patch @@ -0,0 +1,623 @@ +From 28907c1a50132c9a308b8d2c22bf4905041c446f Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 30 Sep 2013 15:49:00 +0200 +Subject: [PATCH 64/84] bash-completion: use '\n' as IFS when ask for filenames +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The bash completion for more(1) treats the space-separated pieces of +filenames as different files. + + $ touch foo\ bar + $ more foo<TAB> + bar foo + +Upstream: http://github.com/karelzak/util-linux/commit/ce3e6b15e2c4478b2df9a7016c168b16325abfb0 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1296366 +Reported-by: Ángel González <ingenit@zoho.com> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + bash-completion/blkid | 1 + + bash-completion/colcrt | 1 + + bash-completion/column | 1 + + bash-completion/cytune | 1 + + bash-completion/dmesg | 1 + + bash-completion/fallocate | 1 + + bash-completion/findmnt | 1 + + bash-completion/flock | 1 + + bash-completion/fsck.cramfs | 1 + + bash-completion/hexdump | 1 + + bash-completion/hwclock | 1 + + bash-completion/ionice | 1 + + bash-completion/last | 1 + + bash-completion/ldattach | 1 + + bash-completion/logger | 1 + + bash-completion/look | 1 + + bash-completion/losetup | 1 + + bash-completion/lscpu | 1 + + bash-completion/lslocks | 1 + + bash-completion/mcookie | 1 + + bash-completion/mkfs.cramfs | 1 + + bash-completion/mkswap | 1 + + bash-completion/more | 2 ++ + bash-completion/mountpoint | 1 + + bash-completion/namei | 1 + + bash-completion/nsenter | 1 + + bash-completion/pg | 1 + + bash-completion/pivot_root | 1 + + bash-completion/readprofile | 1 + + bash-completion/rename | 1 + + bash-completion/rev | 1 + + bash-completion/script | 1 + + bash-completion/scriptreplay | 1 + + bash-completion/setterm | 1 + + bash-completion/sfdisk | 1 + + bash-completion/su | 1 + + bash-completion/swaplabel | 1 + + bash-completion/tailf | 1 + + bash-completion/tunelp | 1 + + bash-completion/ul | 1 + + bash-completion/utmpdump | 1 + + bash-completion/uuidd | 3 +++ + bash-completion/wall | 1 + + bash-completion/wdctl | 1 + + bash-completion/whereis | 1 + + 45 files changed, 48 insertions(+) + +diff --git a/bash-completion/blkid b/bash-completion/blkid +index b439328..9f97dd9 100644 +--- a/bash-completion/blkid ++++ b/bash-completion/blkid +@@ -6,6 +6,7 @@ _blkid_module() + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-c') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/colcrt b/bash-completion/colcrt +index f9e4c33..c66d7e6 100644 +--- a/bash-completion/colcrt ++++ b/bash-completion/colcrt +@@ -19,6 +19,7 @@ _colcrt_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/column b/bash-completion/column +index f5cb86b..3af8e73 100644 +--- a/bash-completion/column ++++ b/bash-completion/column +@@ -30,6 +30,7 @@ _column_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/cytune b/bash-completion/cytune +index 4f42838..f685c88 100644 +--- a/bash-completion/cytune ++++ b/bash-completion/cytune +@@ -33,6 +33,7 @@ _cytune_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- ${cur:-"/dev/tty"}) ) + return 0 +diff --git a/bash-completion/dmesg b/bash-completion/dmesg +index 60ecc1a..eab41ba 100644 +--- a/bash-completion/dmesg ++++ b/bash-completion/dmesg +@@ -6,6 +6,7 @@ _dmesg_module() + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-F'|'--file') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/fallocate b/bash-completion/fallocate +index 5007b60..2c6e4cb 100644 +--- a/bash-completion/fallocate ++++ b/bash-completion/fallocate +@@ -20,6 +20,7 @@ _fallocate_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/findmnt b/bash-completion/findmnt +index 9386d8f..3ed331a 100644 +--- a/bash-completion/findmnt ++++ b/bash-completion/findmnt +@@ -18,6 +18,7 @@ _findmnt_module() + return 0 + ;; + '-F'|'--tab-file') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/flock b/bash-completion/flock +index 8cd60d3..8e7f8b8 100644 +--- a/bash-completion/flock ++++ b/bash-completion/flock +@@ -38,6 +38,7 @@ _flock_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- ${cur:-"/"}) ) + return 0 +diff --git a/bash-completion/fsck.cramfs b/bash-completion/fsck.cramfs +index 84f6f31..acf6564 100644 +--- a/bash-completion/fsck.cramfs ++++ b/bash-completion/fsck.cramfs +@@ -6,6 +6,7 @@ _fsck.cramfs_module() + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-x'|'--destination') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -o dirnames -- ${cur:-"/"}) ) + return 0 +diff --git a/bash-completion/hexdump b/bash-completion/hexdump +index 0c91187..c17bcae 100644 +--- a/bash-completion/hexdump ++++ b/bash-completion/hexdump +@@ -28,6 +28,7 @@ _hexdump_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/hwclock b/bash-completion/hwclock +index 0c4ebaf..de1ac20 100644 +--- a/bash-completion/hwclock ++++ b/bash-completion/hwclock +@@ -6,6 +6,7 @@ _hwclock_module() + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-f'|'--rtc'|'--adjfile') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/ionice b/bash-completion/ionice +index 3a01c51..1b1c5fe 100644 +--- a/bash-completion/ionice ++++ b/bash-completion/ionice +@@ -30,6 +30,7 @@ _ionice_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/last b/bash-completion/last +index 493051e..c93be3d 100644 +--- a/bash-completion/last ++++ b/bash-completion/last +@@ -6,6 +6,7 @@ _last_module() + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-f') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/ldattach b/bash-completion/ldattach +index 830142d..0b9d260 100644 +--- a/bash-completion/ldattach ++++ b/bash-completion/ldattach +@@ -42,6 +42,7 @@ _ldattach_module() + return 0 + ;; + /*) ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/logger b/bash-completion/logger +index f46be8a..963abc7 100644 +--- a/bash-completion/logger ++++ b/bash-completion/logger +@@ -6,6 +6,7 @@ _logger_module() + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-f'|'--file') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/look b/bash-completion/look +index e8676ba..303a756 100644 +--- a/bash-completion/look ++++ b/bash-completion/look +@@ -20,6 +20,7 @@ _look_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/losetup b/bash-completion/losetup +index 75240b8..874c549 100644 +--- a/bash-completion/losetup ++++ b/bash-completion/losetup +@@ -60,6 +60,7 @@ _losetup_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/lscpu b/bash-completion/lscpu +index bce07c4..244b418 100644 +--- a/bash-completion/lscpu ++++ b/bash-completion/lscpu +@@ -41,6 +41,7 @@ _lscpu_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/lslocks b/bash-completion/lslocks +index 337d07e..c9cff2c 100644 +--- a/bash-completion/lslocks ++++ b/bash-completion/lslocks +@@ -38,6 +38,7 @@ _lslocks_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/mcookie b/bash-completion/mcookie +index eb3f54b..4345b6e 100644 +--- a/bash-completion/mcookie ++++ b/bash-completion/mcookie +@@ -6,6 +6,7 @@ _mcookie_module() + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-f'|'--file') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/mkfs.cramfs b/bash-completion/mkfs.cramfs +index 38ab3b6..af74e12 100644 +--- a/bash-completion/mkfs.cramfs ++++ b/bash-completion/mkfs.cramfs +@@ -36,6 +36,7 @@ _mkfs.cramfs_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/mkswap b/bash-completion/mkswap +index c411b30..b72efaf 100644 +--- a/bash-completion/mkswap ++++ b/bash-completion/mkswap +@@ -28,6 +28,7 @@ _mkswap_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/more b/bash-completion/more +index 945c7b3..809cea2 100644 +--- a/bash-completion/more ++++ b/bash-completion/more +@@ -21,6 +21,8 @@ _more_module() + return 0 + ;; + esac ++ ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/mountpoint b/bash-completion/mountpoint +index 8fe27b8..f74efc9 100644 +--- a/bash-completion/mountpoint ++++ b/bash-completion/mountpoint +@@ -20,6 +20,7 @@ _mountpoint_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- ${cur:-"/"}) ) + return 0 +diff --git a/bash-completion/namei b/bash-completion/namei +index 63fb37a..6402735 100644 +--- a/bash-completion/namei ++++ b/bash-completion/namei +@@ -16,6 +16,7 @@ _namei_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/nsenter b/bash-completion/nsenter +index 2970b8d..268f378 100644 +--- a/bash-completion/nsenter ++++ b/bash-completion/nsenter +@@ -40,6 +40,7 @@ _nsenter_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/pg b/bash-completion/pg +index 9b1bad9..8fce130 100644 +--- a/bash-completion/pg ++++ b/bash-completion/pg +@@ -25,6 +25,7 @@ _pg_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/pivot_root b/bash-completion/pivot_root +index 961c883..95df4b4 100644 +--- a/bash-completion/pivot_root ++++ b/bash-completion/pivot_root +@@ -11,6 +11,7 @@ _pivot_root_module() + esac + case $COMP_CWORD in + 1|2) ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -o dirnames -- ${cur:-"/"}) ) + ;; +diff --git a/bash-completion/readprofile b/bash-completion/readprofile +index a5f45f5..bd265a6 100644 +--- a/bash-completion/readprofile ++++ b/bash-completion/readprofile +@@ -6,6 +6,7 @@ _readprofile_module() + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-m'|'--mapfile'|'-p'|'--profile') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/rename b/bash-completion/rename +index 3842c4d..81365a5 100644 +--- a/bash-completion/rename ++++ b/bash-completion/rename +@@ -24,6 +24,7 @@ _rename_module() + COMPREPLY=( $(compgen -W "replacement" -- $cur) ) + ;; + *) ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + ;; +diff --git a/bash-completion/rev b/bash-completion/rev +index 619c5c4..e5397d5 100644 +--- a/bash-completion/rev ++++ b/bash-completion/rev +@@ -16,6 +16,7 @@ _rev_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/script b/bash-completion/script +index 329fc48..d7efd7e 100644 +--- a/bash-completion/script ++++ b/bash-completion/script +@@ -32,6 +32,7 @@ _script_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/scriptreplay b/bash-completion/scriptreplay +index 2ad7b11..a4aa8a0 100644 +--- a/bash-completion/scriptreplay ++++ b/bash-completion/scriptreplay +@@ -24,6 +24,7 @@ _scriptreplay_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/setterm b/bash-completion/setterm +index 7fa0a4e..a7ef6eb 100644 +--- a/bash-completion/setterm ++++ b/bash-completion/setterm +@@ -45,6 +45,7 @@ _setterm_module() + return 0 + ;; + '-file') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/sfdisk b/bash-completion/sfdisk +index 0226b04..609104c 100644 +--- a/bash-completion/sfdisk ++++ b/bash-completion/sfdisk +@@ -14,6 +14,7 @@ _sfdisk_module() + return 0 + ;; + '-O'|'-I') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/su b/bash-completion/su +index e739b56..dad1b5f 100644 +--- a/bash-completion/su ++++ b/bash-completion/su +@@ -38,6 +38,7 @@ _su_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/swaplabel b/bash-completion/swaplabel +index 093169e..08aa9cd 100644 +--- a/bash-completion/swaplabel ++++ b/bash-completion/swaplabel +@@ -24,6 +24,7 @@ _swaplabel_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/tailf b/bash-completion/tailf +index e3dd295..0d4c869 100644 +--- a/bash-completion/tailf ++++ b/bash-completion/tailf +@@ -20,6 +20,7 @@ _tailf_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/tunelp b/bash-completion/tunelp +index 614b235..bd2cce2 100644 +--- a/bash-completion/tunelp ++++ b/bash-completion/tunelp +@@ -44,6 +44,7 @@ _tunelp_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- ${cur:-"/dev/lp"}) ) + return 0 +diff --git a/bash-completion/ul b/bash-completion/ul +index c00e510..449cbe0 100644 +--- a/bash-completion/ul ++++ b/bash-completion/ul +@@ -25,6 +25,7 @@ _ul_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/utmpdump b/bash-completion/utmpdump +index 3b868ce..7e4fd5a 100644 +--- a/bash-completion/utmpdump ++++ b/bash-completion/utmpdump +@@ -16,6 +16,7 @@ _utmpdump_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/uuidd b/bash-completion/uuidd +index c45b067..c8f6697 100644 +--- a/bash-completion/uuidd ++++ b/bash-completion/uuidd +@@ -6,16 +6,19 @@ _uuidd_module() + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-p'|'--pid'|'-s'|'--socket') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 + ;; + '-T'|'--timeout') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -W "timeout" -- $cur) ) + return 0 + ;; + '-n'|'--uuids') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -W "number" -- $cur) ) + return 0 +diff --git a/bash-completion/wall b/bash-completion/wall +index 55d9658..e3145ff 100644 +--- a/bash-completion/wall ++++ b/bash-completion/wall +@@ -20,6 +20,7 @@ _wall_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- $cur) ) + return 0 +diff --git a/bash-completion/wdctl b/bash-completion/wdctl +index 33b4e1f..4f16e76 100644 +--- a/bash-completion/wdctl ++++ b/bash-completion/wdctl +@@ -56,6 +56,7 @@ _wdctl_module() + return 0 + ;; + esac ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -f -- ${cur:-"/dev/"}) ) + return 0 +diff --git a/bash-completion/whereis b/bash-completion/whereis +index 2273a07..0dcbac3 100644 +--- a/bash-completion/whereis ++++ b/bash-completion/whereis +@@ -6,6 +6,7 @@ _whereis_module() + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-B'|'-M'|'-S') ++ local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -o dirnames -- ${cur:-"/"}) ) + return 0 +-- +2.7.4 + diff --git a/SOURCES/0065-hwclock-change-audit-message.patch b/SOURCES/0065-hwclock-change-audit-message.patch new file mode 100644 index 0000000..a50d8e2 --- /dev/null +++ b/SOURCES/0065-hwclock-change-audit-message.patch @@ -0,0 +1,31 @@ +From 19b9d9197374c5811e32777ca70a32eef37a1fb0 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 7 Jan 2016 13:23:24 +0100 +Subject: [PATCH 65/84] hwclock: change audit message + +The preferred layout is name=value for audit messages. + +Upstream: http://github.com/karelzak/util-linux/commit/fbed7e09f826e7804e99522cc1dd3cf54c9cdb67 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1296521 +References: https://bugzilla.redhat.com/show_bug.cgi?id=1296278 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/hwclock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sys-utils/hwclock.c b/sys-utils/hwclock.c +index c7789c8..ac9294b 100644 +--- a/sys-utils/hwclock.c ++++ b/sys-utils/hwclock.c +@@ -1939,7 +1939,7 @@ void __attribute__((__noreturn__)) hwaudit_exit(int status) + { + if (hwaudit_on) { + audit_log_user_message(hwaudit_fd, AUDIT_USYS_CONFIG, +- "changing system time", NULL, NULL, NULL, ++ "op=change-system-time", NULL, NULL, NULL, + status ? 0 : 1); + close(hwaudit_fd); + } +-- +2.7.4 + diff --git a/SOURCES/0066-su-clean-up-groups-initialization.patch b/SOURCES/0066-su-clean-up-groups-initialization.patch new file mode 100644 index 0000000..ebd0225 --- /dev/null +++ b/SOURCES/0066-su-clean-up-groups-initialization.patch @@ -0,0 +1,186 @@ +From b2a41801904c4b281a717dde7f5e146cbd4500b3 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 15 Feb 2016 13:55:37 +0100 +Subject: [PATCH 66/84] su: clean up groups initialization + +This patch does not change any su/runuser behaviour, code changes: + +* don't use huge groups[NGROUPS_MAX]; the array has 256k, but we need + it only occasionally when -G/-g specified. + +* the current code uses groups[0] for -g and the rest for -G, this patch adds + 'gid' to remember -g argument to avoid memmove() + +* add function add_supp_group() to simplify su_main() + +* add note about -G and -g relation to the man pages (undocumented now) + +Upstream: http://github.com/karelzak/util-linux/commit/c619d3d167115990e9228b27851e0cc2faa8f936 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1304426 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + login-utils/runuser.1 | 5 ++-- + login-utils/su-common.c | 68 +++++++++++++++++++++++++++---------------------- + login-utils/su.1 | 5 ++-- + 3 files changed, 44 insertions(+), 34 deletions(-) + +diff --git a/login-utils/runuser.1 b/login-utils/runuser.1 +index 7201ff0..d82dbb0 100644 +--- a/login-utils/runuser.1 ++++ b/login-utils/runuser.1 +@@ -75,8 +75,9 @@ shell. + \fB\-g\fR, \fB\-\-group\fR=\fIgroup\fR\fR + specify the primary group, this option is allowed for root user only + .TP +-\fB\-G\fR, \fB\-\-supp-group\fR=\fIgroup\fR\fR +-specify a supplemental group, this option is allowed for root user only ++.BR \-G , " \-\-supp\-group" = \fIgroup ++Specify a supplemental group. This option is available to the root user only. The first specified ++supplementary group is also used as a primary group if the option \fB\-\-group\fR is unspecified. + .TP + \fB\-\fR, \fB\-l\fR, \fB\-\-login\fR + Starts the shell as login shell with an environment similar to a real +diff --git a/login-utils/su-common.c b/login-utils/su-common.c +index dd87804..d53d690 100644 +--- a/login-utils/su-common.c ++++ b/login-utils/su-common.c +@@ -535,7 +535,7 @@ modify_environment (const struct passwd *pw, const char *shell) + /* Become the user and group(s) specified by PW. */ + + static void +-init_groups (const struct passwd *pw, gid_t *groups, int num_groups) ++init_groups (const struct passwd *pw, gid_t *groups, size_t num_groups) + { + int retval; + +@@ -707,6 +707,28 @@ evaluate_uid(void) + return (uid_t) 0 == ruid && ruid == euid ? 0 : 1; + } + ++static gid_t ++add_supp_group(const char *name, gid_t **groups, size_t *ngroups) ++{ ++ struct group *gr; ++ ++ if (*ngroups >= NGROUPS_MAX) ++ errx(EXIT_FAILURE, ++ P_("specifying more than %d supplemental group is not possible", ++ "specifying more than %d supplemental groups is not possible", ++ NGROUPS_MAX - 1), NGROUPS_MAX - 1); ++ ++ gr = getgrnam(name); ++ if (!gr) ++ errx(EXIT_FAILURE, _("group %s does not exist"), name); ++ ++ *groups = xrealloc(*groups, sizeof(gid_t) * (*ngroups + 1)); ++ (*groups)[*ngroups] = gr->gr_gid; ++ (*ngroups)++; ++ ++ return gr->gr_gid; ++} ++ + int + su_main (int argc, char **argv, int mode) + { +@@ -717,10 +739,12 @@ su_main (int argc, char **argv, int mode) + char *shell = NULL; + struct passwd *pw; + struct passwd pw_copy; +- struct group *gr; +- gid_t groups[NGROUPS_MAX]; +- int num_supp_groups = 0; +- int use_gid = 0; ++ ++ gid_t *groups = NULL; ++ size_t ngroups = 0; ++ bool use_supp = false; ++ bool use_gid = false; ++ gid_t gid = 0; + + static const struct option longopts[] = { + {"command", required_argument, NULL, 'c'}, +@@ -765,23 +789,13 @@ su_main (int argc, char **argv, int mode) + break; + + case 'g': +- gr = getgrnam(optarg); +- if (!gr) +- errx(EXIT_FAILURE, _("group %s does not exist"), optarg); +- use_gid = 1; +- groups[0] = gr->gr_gid; ++ use_gid = true; ++ gid = add_supp_group(optarg, &groups, &ngroups); + break; + + case 'G': +- num_supp_groups++; +- if (num_supp_groups >= NGROUPS_MAX) +- errx(EXIT_FAILURE, +- _("can't specify more than %d supplemental groups"), +- NGROUPS_MAX - 1); +- gr = getgrnam(optarg); +- if (!gr) +- errx(EXIT_FAILURE, _("group %s does not exist"), optarg); +- groups[num_supp_groups] = gr->gr_gid; ++ use_supp = true; ++ add_supp_group(optarg, &groups, &ngroups); + break; + + case 'l': +@@ -852,7 +866,7 @@ su_main (int argc, char **argv, int mode) + break; + } + +- if ((num_supp_groups || use_gid) && restricted) ++ if ((use_supp || use_gid) && restricted) + errx(EXIT_FAILURE, _("only root can specify alternative groups")); + + logindefs_load_defaults = load_config; +@@ -878,16 +892,10 @@ su_main (int argc, char **argv, int mode) + : DEFAULT_SHELL); + endpwent (); + +- if (num_supp_groups && !use_gid) +- { +- pw->pw_gid = groups[1]; +- memmove (groups, groups + 1, sizeof(gid_t) * num_supp_groups); +- } +- else if (use_gid) +- { ++ if (use_supp && !use_gid) + pw->pw_gid = groups[0]; +- num_supp_groups++; +- } ++ else if (use_gid) ++ pw->pw_gid = gid; + + authenticate (pw); + +@@ -912,7 +920,7 @@ su_main (int argc, char **argv, int mode) + shell = xstrdup (shell ? shell : pw->pw_shell); + } + +- init_groups (pw, groups, num_supp_groups); ++ init_groups (pw, groups, ngroups); + + if (!simulate_login || command) + suppress_pam_info = 1; /* don't print PAM info messages */ +diff --git a/login-utils/su.1 b/login-utils/su.1 +index eab1a6f..1f69868 100644 +--- a/login-utils/su.1 ++++ b/login-utils/su.1 +@@ -62,8 +62,9 @@ shell. + \fB\-g\fR, \fB\-\-group\fR=\fIgroup\fR\fR + specify the primary group, this option is allowed for root user only + .TP +-\fB\-G\fR, \fB\-\-supp-group\fR=\fIgroup\fR\fR +-specify a supplemental group, this option is allowed for root user only ++.BR \-G , " \-\-supp\-group" = \fIgroup ++Specify a supplemental group. This option is available to the root user only. The first specified ++supplementary group is also used as a primary group if the option \fB\-\-group\fR is unspecified. + .TP + \fB\-\fR, \fB\-l\fR, \fB\-\-login\fR + Starts the shell as login shell with an environment similar to a real +-- +2.7.4 + diff --git a/SOURCES/0067-lscpu-Fix-model-and-model-name-on-Power-Systems.patch b/SOURCES/0067-lscpu-Fix-model-and-model-name-on-Power-Systems.patch new file mode 100644 index 0000000..9bf633c --- /dev/null +++ b/SOURCES/0067-lscpu-Fix-model-and-model-name-on-Power-Systems.patch @@ -0,0 +1,83 @@ +From 246ab88515fca389c02602521fc765d3e597fd7a Mon Sep 17 00:00:00 2001 +From: Vasant Hegde <hegdevasant@linux.vnet.ibm.com> +Date: Mon, 14 Mar 2016 20:18:07 +0530 +Subject: [PATCH 67/84] lscpu: Fix model and model name on Power Systems + +On Power System, lspcu presently displays system model number instead of +processor model name. 'model' tag in cpuinfo contains system model name, +not processor model. Instead it uses 'cpu' tag for processor model name. +Also it uses 'revision' tag for processor model. + +Fix lspcu so that it displays processor model number. Also display processor +model name. + +cpuinfo output on Power System: + ... + ... + + processor : 127 + cpu : POWER8E (raw), altivec supported + clock : 4322.000000MHz + revision : 2.1 (pvr 004b 0201) + + timebase : 512000000 + platform : PowerNV + model : 8286-42A + machine : PowerNV 8286-42A + firmware : OPAL + +Output without this patch: + Architecture: ppc64le + Byte Order: Little Endian + CPU(s): 128 + On-line CPU(s) list: 0-127 + Thread(s) per core: 8 + Core(s) per socket: 4 + Socket(s): 4 + NUMA node(s): 4 + Model: 8286-42A + ... + ... + +Output with this patch: + Architecture: ppc64le + Byte Order: Little Endian + CPU(s): 128 + On-line CPU(s) list: 0-127 + Thread(s) per core: 8 + Core(s) per socket: 4 + Socket(s): 4 + NUMA node(s): 4 + Model: 2.1 (pvr 004b 0201) + Model name: POWER8E (raw), altivec supported + ... + ... + +Upstream: http://github.com/karelzak/util-linux/commit/3ac03fe4d20558b55635a048d7f2fb0f5e85ee2a +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1326615 +Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com> +--- + sys-utils/lscpu.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c +index 4af8176..68b15af 100644 +--- a/sys-utils/lscpu.c ++++ b/sys-utils/lscpu.c +@@ -355,8 +355,13 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod) + else if (lookup(buf, "vendor_id", &desc->vendor)) ; + else if (lookup(buf, "family", &desc->family)) ; + else if (lookup(buf, "cpu family", &desc->family)) ; ++#if defined(__powerpc__) || defined(__powerpc64__) ++ else if (lookup(buf, "revision", &desc->model)) ; ++ else if (lookup(buf, "cpu", &desc->modelname)) ; ++#else + else if (lookup(buf, "model", &desc->model)) ; + else if (lookup(buf, "model name", &desc->modelname)) ; ++#endif + else if (lookup(buf, "stepping", &desc->stepping)) ; + else if (lookup(buf, "cpu MHz", &desc->mhz)) ; + else if (lookup(buf, "flags", &desc->flags)) ; /* x86 */ +-- +2.7.4 + diff --git a/SOURCES/0068-lscpu-use-cpu-and-revision-tag-if-available.patch b/SOURCES/0068-lscpu-use-cpu-and-revision-tag-if-available.patch new file mode 100644 index 0000000..c1962e2 --- /dev/null +++ b/SOURCES/0068-lscpu-use-cpu-and-revision-tag-if-available.patch @@ -0,0 +1,105 @@ +From cd0d8ef86151d72a246d565844d4c0470feb6b20 Mon Sep 17 00:00:00 2001 +From: Ruediger Meier <ruediger.meier@ga-group.nl> +Date: Wed, 16 Mar 2016 13:18:18 +0100 +Subject: [PATCH 68/84] lscpu: use cpu and revision tag if available + +Avoid ifdef which does not work with --sysroot. Our existing test +dumps produce even better output now for ppc and sparc. + +The logic moved to the printing section. + +Upstream: http://github.com/karelzak/util-linux/commit/641350fe822e7f1ac10873dad9a364bdeaba8083 +Upstream: http://github.com/karelzak/util-linux/commit/86c4817e0ea02656ddb62fe27757a9fd4f13b2d3 +Upstream: http://github.com/karelzak/util-linux/commit/c95e3889725389e9d7e24d29c2a71b015959575f +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1326615 +CC: Vasant Hegde <hegdevasant@linux.vnet.ibm.com> +Signed-off-by: Ruediger Meier <ruediger.meier@ga-group.nl> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/lscpu.c | 17 ++++++++--------- + tests/expected/lscpu/lscpu-ppc64-POWER7 | 3 ++- + tests/expected/lscpu/lscpu-ppc64-POWER7-64cpu | 3 ++- + 3 files changed, 12 insertions(+), 11 deletions(-) + +diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c +index 68b15af..7a00636 100644 +--- a/sys-utils/lscpu.c ++++ b/sys-utils/lscpu.c +@@ -141,6 +141,8 @@ struct lscpu_desc { + char *family; + char *model; + char *modelname; ++ char *revision; /* alternative for model (ppc) */ ++ char *cpu; /* alternative for modelname (ppc, sparc) */ + char *virtflag; /* virtualization flag (vmx, svm) */ + char *hypervisor; /* hypervisor software */ + int hyper; /* hypervisor vendor ID */ +@@ -355,13 +357,8 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod) + else if (lookup(buf, "vendor_id", &desc->vendor)) ; + else if (lookup(buf, "family", &desc->family)) ; + else if (lookup(buf, "cpu family", &desc->family)) ; +-#if defined(__powerpc__) || defined(__powerpc64__) +- else if (lookup(buf, "revision", &desc->model)) ; +- else if (lookup(buf, "cpu", &desc->modelname)) ; +-#else + else if (lookup(buf, "model", &desc->model)) ; + else if (lookup(buf, "model name", &desc->modelname)) ; +-#endif + else if (lookup(buf, "stepping", &desc->stepping)) ; + else if (lookup(buf, "cpu MHz", &desc->mhz)) ; + else if (lookup(buf, "flags", &desc->flags)) ; /* x86 */ +@@ -369,6 +366,8 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod) + else if (lookup(buf, "type", &desc->flags)) ; /* sparc64 */ + else if (lookup(buf, "bogomips", &desc->bogomips)) ; + else if (lookup(buf, "bogomips per cpu", &desc->bogomips)) ; /* s390 */ ++ else if (lookup(buf, "cpu", &desc->cpu)) ; ++ else if (lookup(buf, "revision", &desc->revision)) ; + else + continue; + } +@@ -1264,10 +1263,10 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod) + print_s(_("Vendor ID:"), desc->vendor); + if (desc->family) + print_s(_("CPU family:"), desc->family); +- if (desc->model) +- print_s(_("Model:"), desc->model); +- if (desc->modelname) +- print_s(_("Model name:"), desc->modelname); ++ if (desc->model || desc->revision) ++ print_s(_("Model:"), desc->revision ? desc->revision : desc->model); ++ if (desc->modelname || desc->cpu) ++ print_s(_("Model name:"), desc->cpu ? desc->cpu : desc->modelname); + if (desc->stepping) + print_s(_("Stepping:"), desc->stepping); + if (desc->mhz) +diff --git a/tests/expected/lscpu/lscpu-ppc64-POWER7 b/tests/expected/lscpu/lscpu-ppc64-POWER7 +index 0d6c5ba..9a3c0c9 100644 +--- a/tests/expected/lscpu/lscpu-ppc64-POWER7 ++++ b/tests/expected/lscpu/lscpu-ppc64-POWER7 +@@ -4,7 +4,8 @@ Thread(s) per core: 4 + Core(s) per socket: 1 + Socket(s): 4 + NUMA node(s): 1 +-Model: IBM,8233-E8B ++Model: 2.1 (pvr 003f 0201) ++Model name: POWER7 (architected), altivec supported + L1d cache: 32K + L1i cache: 32K + NUMA node0 CPU(s): 0-15 +diff --git a/tests/expected/lscpu/lscpu-ppc64-POWER7-64cpu b/tests/expected/lscpu/lscpu-ppc64-POWER7-64cpu +index 40e2736..d4ae6c1 100644 +--- a/tests/expected/lscpu/lscpu-ppc64-POWER7-64cpu ++++ b/tests/expected/lscpu/lscpu-ppc64-POWER7-64cpu +@@ -4,7 +4,8 @@ Thread(s) per core: 4 + Core(s) per socket: 1 + Socket(s): 16 + NUMA node(s): 2 +-Model: IBM,8231-E2B ++Model: 2.1 (pvr 003f 0201) ++Model name: POWER7 (architected), altivec supported + L1d cache: 32K + L1i cache: 32K + NUMA node0 CPU(s): 0-63 +-- +2.7.4 + diff --git a/SOURCES/0069-findfs-add-ability-to-work-with-PART-UUID-LABEL-too.patch b/SOURCES/0069-findfs-add-ability-to-work-with-PART-UUID-LABEL-too.patch new file mode 100644 index 0000000..48c3db4 --- /dev/null +++ b/SOURCES/0069-findfs-add-ability-to-work-with-PART-UUID-LABEL-too.patch @@ -0,0 +1,137 @@ +From 2555bd3bad9ea8e7ae40a727f59bb546d2aa2717 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 28 Mar 2014 10:36:05 +0100 +Subject: [PATCH 69/84] findfs: add ability to work with PART{UUID,LABEL}= too + +Upstream: http://github.com/karelzak/util-linux/commit/c48508c2faa356c48c26d7d0070a6f20ae4ba9a0 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1335671 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + misc-utils/findfs.8 | 51 +++++++++++++++++++++++++++++++++++++++------------ + misc-utils/findfs.c | 17 +++++------------ + 2 files changed, 44 insertions(+), 24 deletions(-) + +diff --git a/misc-utils/findfs.8 b/misc-utils/findfs.8 +index 8a6bca1..b92cd45 100644 +--- a/misc-utils/findfs.8 ++++ b/misc-utils/findfs.8 +@@ -7,19 +7,45 @@ + findfs \- find a filesystem by label or UUID + .SH SYNOPSIS + .B findfs +-.BI LABEL= label +-.sp +-.B findfs +-.BI UUID= uuid ++.BI NAME= value + .SH DESCRIPTION + .B findfs +-will search the disks in the system looking for a filesystem which has +-a label matching +-.I label +-or a UUID equal to +-.IR uuid . +-If the filesystem is found, the device name for the filesystem will +-be printed on stdout. ++will search the block devices in the system looking for a filesystem or ++partition with specified tag. The currently supported tags are: ++.TP ++.B LABEL=<label> ++Specifies filesystem label. ++.TP ++.B UUID=<uuid> ++Specifies filesystem UUID. ++.TP ++.B PARTUUID=<uuid> ++Specifies partition UUID. This partition identifier is supported for example for ++GUID Partition Table (GPT) partition tables. ++.TP ++.B PARTLABEL=<label> ++Specifies partition label (name). The partition labels are supported for example for ++GUID Partition Table (GPT) or MAC partition tables. ++.PP ++If the filesystem or partition is found, the device name will be printed on ++stdout. ++ ++The complete overview about filesystems and partitions you can get for example ++by ++.RS ++ ++.br ++.BI "lsblk \-\-fs" ++.br ++ ++.BI "partx --show <disk>" ++.br ++ ++.BI blkid ++.br ++ ++.RE ++ + .PP + .SH AUTHOR + .B findfs +@@ -30,7 +56,8 @@ the util-linux package by Karel Zak (kzak@redhat.com). + enables debug output. + .SH SEE ALSO + .BR blkid (8), +-.BR fsck (8) ++.BR lsblk (8), ++.BR partx (8) + .SH AVAILABILITY + The findfs command is part of the util-linux package and is available from + ftp://ftp.kernel.org/pub/linux/utils/util-linux/. +diff --git a/misc-utils/findfs.c b/misc-utils/findfs.c +index bc4a843..29ca1cb 100644 +--- a/misc-utils/findfs.c ++++ b/misc-utils/findfs.c +@@ -19,8 +19,7 @@ static void __attribute__((__noreturn__)) usage(int rc) + { + FILE *out = rc ? stderr : stdout; + fputs(USAGE_HEADER, out); +- fprintf(out, _(" %1$s [options] LABEL=<label>\n" +- " %1$s [options] UUID=<uuid>\n"), ++ fprintf(out, _(" %s [options] {LABEL,UUID,PARTUUID,PARTLABEL}=<value>\n"), + program_invocation_short_name); + fputs(USAGE_OPTIONS, out); + fputs(USAGE_HELP, out); +@@ -31,7 +30,7 @@ static void __attribute__((__noreturn__)) usage(int rc) + + int main(int argc, char **argv) + { +- char *dev, *tk, *vl; ++ char *dev; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); +@@ -43,23 +42,17 @@ int main(int argc, char **argv) + * with version from e2fsprogs */ + usage(2); + +- if (!strncmp(argv[1], "LABEL=", 6)) { +- tk = "LABEL"; +- vl = argv[1] + 6; +- } else if (!strncmp(argv[1], "UUID=", 5)) { +- tk = "UUID"; +- vl = argv[1] + 5; +- } else if (strcmp(argv[1], "-V") == 0 || ++ if (strcmp(argv[1], "-V") == 0 || + strcmp(argv[1], "--version") == 0) { + printf(UTIL_LINUX_VERSION); + return EXIT_SUCCESS; + } else if (strcmp(argv[1], "-h") == 0 || + strcmp(argv[1], "--help") == 0) { + usage(EXIT_SUCCESS); +- } else ++ } else if (argv[1][0] == '-') + usage(2); + +- dev = blkid_evaluate_tag(tk, vl, NULL); ++ dev = blkid_evaluate_tag(argv[1], NULL, NULL); + if (!dev) + errx(EXIT_FAILURE, _("unable to resolve '%s'"), argv[1]); + +-- +2.7.4 + diff --git a/SOURCES/0070-libblkid-fix-memory-leak-in-blkid_parse_tag_string.patch b/SOURCES/0070-libblkid-fix-memory-leak-in-blkid_parse_tag_string.patch new file mode 100644 index 0000000..4453744 --- /dev/null +++ b/SOURCES/0070-libblkid-fix-memory-leak-in-blkid_parse_tag_string.patch @@ -0,0 +1,44 @@ +From 8af4232ff50154588c75f25a951b5619f05d0421 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 19 Nov 2013 17:52:56 +0100 +Subject: [PATCH 70/84] libblkid: fix memory leak in blkid_parse_tag_string() + +Upstream: http://github.com/karelzak/util-linux/commit/c1178175e7adb35388a3e34495974d7f24f45d5d +Upstream: http://github.com/karelzak/util-linux/commit/aab691cf8deb4a53782a0317b6f96c5d8b61f8e9 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1335671 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/tag.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/libblkid/src/tag.c b/libblkid/src/tag.c +index 3a70950..3aede04 100644 +--- a/libblkid/src/tag.c ++++ b/libblkid/src/tag.c +@@ -237,14 +237,18 @@ int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val) + goto errout; /* missing closing quote */ + *cp = '\0'; + } +- value = value && *value ? strdup(value) : NULL; +- if (!value) +- goto errout; ++ ++ if (ret_val) { ++ value = *value ? strdup(value) : NULL; ++ if (!value) ++ goto errout; ++ *ret_val = value; ++ } + + if (ret_type) + *ret_type = name; +- if (ret_val) +- *ret_val = value; ++ else ++ free(name); + + return 0; + +-- +2.7.4 + diff --git a/SOURCES/0071-findmnt-don-t-rely-on-st_dev-for-target.patch b/SOURCES/0071-findmnt-don-t-rely-on-st_dev-for-target.patch new file mode 100644 index 0000000..7d1bc7f --- /dev/null +++ b/SOURCES/0071-findmnt-don-t-rely-on-st_dev-for-target.patch @@ -0,0 +1,104 @@ +From 0f66811659aa8fd8b14ade8a80bfecd580962b2d Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 10 Mar 2015 12:51:44 +0100 +Subject: [PATCH 71/84] findmnt: don't rely on st_dev for --target + +The overlay filesystem does not provide usable st_dev (in traditional +UNIX way). It's necessary to search in /proc/self/mountinfo to detect +which path element is mountpoint. + +$ findmnt --target /mnt/merged/dir-a/foo +TARGET SOURCE FSTYPE OPTIONS +/mnt/merged overlay overlay rw,relatime,lowerdir=/mnt/low,upperdir=/mnt/high/data,workdir=/mnt/high/work + +Upstream: http://github.com/karelzak/util-linux/commit/cd41b385a06dde70bb45c3143d3459157bda58f8 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=587393 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libmount/src/utils.c | 6 +++++- + misc-utils/findmnt.8 | 8 ++++++-- + misc-utils/findmnt.c | 17 +++++++++++------ + 3 files changed, 22 insertions(+), 9 deletions(-) + +diff --git a/libmount/src/utils.c b/libmount/src/utils.c +index 7c6f5b1..5783d88 100644 +--- a/libmount/src/utils.c ++++ b/libmount/src/utils.c +@@ -859,7 +859,11 @@ int mnt_open_uniq_filename(const char *filename, char **name) + * This function finds the mountpoint that a given path resides in. @path + * should be canonicalized. The returned pointer should be freed by the caller. + * +- * Returns: allocated string with target of the mounted device or NULL on error ++ * WARNING: the function compares st_dev of the @path elements. This traditional ++ * way maybe be insufficient on filesystems like Linux "overlay". See also ++ * mnt_table_find_target(). ++ * ++ * Returns: allocated string with the target of the mounted device or NULL on error + */ + char *mnt_get_mountpoint(const char *path) + { +diff --git a/misc-utils/findmnt.8 b/misc-utils/findmnt.8 +index 407636e..54739b7 100644 +--- a/misc-utils/findmnt.8 ++++ b/misc-utils/findmnt.8 +@@ -179,8 +179,12 @@ Search in + .IR /etc/fstab . + The output is in the list format (see \fB--list\fR). + .TP +-.BR \-T , " \-\-target \fIdir\fP" +-Explicitly define the mount target (mountpoint directory). ++.BR \-T , " \-\-target \fIpath\fP" ++Explicitly define the mount target (mountpoint directory). If the \fIpath\fR ++is not a mountpoint file or directory than ++.B findmnt ++checks \fIpath\fR elements in reverse order for get the mountpoint (this feature is ++supported only if search in kernel files and unsupported for \fB\-\-fstab\fP). + .TP + .BR \-t , " \-\-types \fIlist\fP" + Limit the set of printed filesystems. More than one type may be +diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c +index f16da91..fe899db 100644 +--- a/misc-utils/findmnt.c ++++ b/misc-utils/findmnt.c +@@ -236,9 +236,12 @@ static void set_source_match(const char *data) + set_match(COL_SOURCE, data); + } + +-static void enable_extra_target_match(void) ++/* @tb has to be from kernel (so no fstab or so)! */ ++static void enable_extra_target_match(struct libmnt_table *tb) + { +- char *cn = NULL, *mnt = NULL; ++ char *cn = NULL; ++ const char *tgt = NULL, *mnt = NULL; ++ struct libmnt_fs *fs; + + /* + * Check if match pattern is mountpoint, if not use the +@@ -248,9 +251,11 @@ static void enable_extra_target_match(void) + if (!cn) + return; + +- mnt = mnt_get_mountpoint(cn); +- if (!mnt || strcmp(mnt, cn) == 0) +- return; ++ fs = mnt_table_find_mountpoint(tb, tgt, MNT_ITER_BACKWARD); ++ if (fs) ++ mnt = mnt_fs_get_target(fs); ++ if (mnt && strcmp(mnt, tgt) != 0) ++ set_match(COL_TARGET, xstrdup(mnt)); /* replace the current setting */ + + /* replace the current setting with the real mountpoint */ + set_match(COL_TARGET, mnt); +@@ -1484,7 +1489,7 @@ int main(int argc, char *argv[]) + * try it again with extra functionality for target + * match + */ +- enable_extra_target_match(); ++ enable_extra_target_match(tb); + rc = add_matching_lines(tb, tt, direction); + } + } +-- +2.7.4 + diff --git a/SOURCES/0072-libmount-cleanup-fs-root-detection-code.patch b/SOURCES/0072-libmount-cleanup-fs-root-detection-code.patch new file mode 100644 index 0000000..b0b881d --- /dev/null +++ b/SOURCES/0072-libmount-cleanup-fs-root-detection-code.patch @@ -0,0 +1,201 @@ +From bb3483bf21b9cbe462caaa74fbc03d2eb7845d74 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 10 Mar 2015 13:35:56 +0100 +Subject: [PATCH 72/84] libmount: cleanup fs root detection code + +Upstream: http://github.com/karelzak/util-linux/commit/cc06a01ec551ed2bcd397a5097165b4434179b34 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=587393 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libmount/src/tab.c | 32 ++++++++++++++++++++----- + libmount/src/utils.c | 34 --------------------------- + tests/expected/libmount/utils-fs-root | 1 - + tests/expected/libmount/utils-fs-root-subdir | 1 - + tests/expected/libmount/utils-fs-root-subdir2 | 1 - + tests/ts/libmount/utils | 12 ---------- + 6 files changed, 26 insertions(+), 55 deletions(-) + delete mode 100644 tests/expected/libmount/utils-fs-root + delete mode 100644 tests/expected/libmount/utils-fs-root-subdir + delete mode 100644 tests/expected/libmount/utils-fs-root-subdir2 + +diff --git a/libmount/src/tab.c b/libmount/src/tab.c +index 1ba1eec..10ee7ce 100644 +--- a/libmount/src/tab.c ++++ b/libmount/src/tab.c +@@ -46,6 +46,8 @@ + #include "mountP.h" + #include "strutils.h" + #include "loopdev.h" ++#include "fileutils.h" ++#include "canonicalize.h" + + static int is_mountinfo(struct libmnt_table *tb); + +@@ -900,6 +902,20 @@ struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb, + return NULL; + } + ++static char *remove_mountpoint_from_path(const char *path, const char *mnt) ++{ ++ char *res; ++ const char *p; ++ size_t sz; ++ ++ sz = strlen(mnt); ++ p = sz > 1 ? path + sz : path; ++ ++ res = *p ? strdup(p) : strdup("/"); ++ DBG(UTILS, mnt_debug("%s fs-root is %s", path, res)); ++ return res; ++} ++ + /* + * tb: /proc/self/mountinfo + * fs: filesystem +@@ -919,7 +935,8 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb, + unsigned long mountflags, + char **fsroot) + { +- char *root = NULL, *mnt = NULL; ++ char *root = NULL; ++ const char *mnt = NULL; + const char *fstype; + struct libmnt_fs *src_fs = NULL; + +@@ -937,10 +954,15 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb, + DBG(TAB, mnt_debug("fs-root for bind")); + + src = xsrc = mnt_resolve_spec(mnt_fs_get_source(fs), tb->cache); +- if (src) +- mnt = mnt_get_mountpoint(src); ++ if (src) { ++ struct libmnt_fs *fs = mnt_table_find_mountpoint(tb, ++ src, MNT_ITER_BACKWARD); ++ if (fs) ++ mnt = mnt_fs_get_target(fs); ++ } ++ + if (mnt) +- root = mnt_get_fs_root(src, mnt); ++ root = remove_mountpoint_from_path(src, mnt); + + if (xsrc && !tb->cache) { + free(xsrc); +@@ -1007,11 +1029,9 @@ dflt: + + DBG(TAB, mnt_debug("FS root result: %s", root)); + +- free(mnt); + return src_fs; + err: + free(root); +- free(mnt); + return NULL; + } + +diff --git a/libmount/src/utils.c b/libmount/src/utils.c +index 5783d88..2151ff9 100644 +--- a/libmount/src/utils.c ++++ b/libmount/src/utils.c +@@ -908,28 +908,6 @@ err: + return NULL; + } + +-char *mnt_get_fs_root(const char *path, const char *mnt) +-{ +- char *m = (char *) mnt, *res; +- const char *p; +- size_t sz; +- +- if (!m) +- m = mnt_get_mountpoint(path); +- if (!m) +- return NULL; +- +- sz = strlen(m); +- p = sz > 1 ? path + sz : path; +- +- if (m != mnt) +- free(m); +- +- res = *p ? strdup(p) : strdup("/"); +- DBG(UTILS, mnt_debug("%s fs-root is %s", path, res)); +- return res; +-} +- + /* + * Search for @name kernel command parametr. + * +@@ -1085,17 +1063,6 @@ int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[]) + return 0; + } + +-int test_fsroot(struct libmnt_test *ts, int argc, char *argv[]) +-{ +- char *path = canonicalize_path(argv[1]), +- *mnt = path ? mnt_get_fs_root(path, NULL) : NULL; +- +- printf("%s: %s\n", argv[1], mnt ? : "unknown"); +- free(mnt); +- free(path); +- return 0; +-} +- + int test_filesystems(struct libmnt_test *ts, int argc, char *argv[]) + { + char **filesystems = NULL; +@@ -1170,7 +1137,6 @@ int main(int argc, char *argv[]) + { "--starts-with", test_startswith, "<string> <prefix>" }, + { "--ends-with", test_endswith, "<string> <prefix>" }, + { "--mountpoint", test_mountpoint, "<path>" }, +- { "--fs-root", test_fsroot, "<path>" }, + { "--cd-parent", test_chdir, "<path>" }, + { "--kernel-cmdline",test_kernel_cmdline, "<option> | <option>=" }, + { "--mkdir", test_mkdir, "<path>" }, +diff --git a/tests/expected/libmount/utils-fs-root b/tests/expected/libmount/utils-fs-root +deleted file mode 100644 +index 7746b28..0000000 +--- a/tests/expected/libmount/utils-fs-root ++++ /dev/null +@@ -1 +0,0 @@ +-/proc: / +diff --git a/tests/expected/libmount/utils-fs-root-subdir b/tests/expected/libmount/utils-fs-root-subdir +deleted file mode 100644 +index 09cdb8d..0000000 +--- a/tests/expected/libmount/utils-fs-root-subdir ++++ /dev/null +@@ -1 +0,0 @@ +-/proc/sys/kernel: /sys/kernel +diff --git a/tests/expected/libmount/utils-fs-root-subdir2 b/tests/expected/libmount/utils-fs-root-subdir2 +deleted file mode 100644 +index 2e8b89a..0000000 +--- a/tests/expected/libmount/utils-fs-root-subdir2 ++++ /dev/null +@@ -1 +0,0 @@ +-/etc: /etc +diff --git a/tests/ts/libmount/utils b/tests/ts/libmount/utils +index 6facaad..89ecf10 100755 +--- a/tests/ts/libmount/utils ++++ b/tests/ts/libmount/utils +@@ -64,18 +64,6 @@ ts_init_subtest "mountpoint-root" + ts_valgrind $TESTPROG --mountpoint / &> $TS_OUTPUT + ts_finalize_subtest + +-ts_init_subtest "fs-root" +-ts_valgrind $TESTPROG --fs-root /proc &> $TS_OUTPUT +-ts_finalize_subtest +- +-ts_init_subtest "fs-root-subdir" +-ts_valgrind $TESTPROG --fs-root /proc/sys/kernel &> $TS_OUTPUT +-ts_finalize_subtest +- +-ts_init_subtest "fs-root-subdir2" +-ts_valgrind $TESTPROG --fs-root /etc &> $TS_OUTPUT +-ts_finalize_subtest +- + ts_init_subtest "kernel-cmdline" + export LIBMOUNT_KERNEL_CMDLINE="$TS_SELF/files/kernel_cmdline" + ts_valgrind $TESTPROG --kernel-cmdline selinux= &>> $TS_OUTPUT +-- +2.7.4 + diff --git a/SOURCES/0073-libmount-mark-overlay-as-pseudo-FS.patch b/SOURCES/0073-libmount-mark-overlay-as-pseudo-FS.patch new file mode 100644 index 0000000..db6ce86 --- /dev/null +++ b/SOURCES/0073-libmount-mark-overlay-as-pseudo-FS.patch @@ -0,0 +1,27 @@ +From 346b97a27e817a001c71d45400f17429bd82b364 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 11 Mar 2015 11:38:07 +0100 +Subject: [PATCH 73/84] libmount: mark overlay as pseudo-FS + +Upstream: http://github.com/karelzak/util-linux/commit/209fd7a74879d37cef3d5d1679bc9cad76b96c7a +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=587393 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libmount/src/utils.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/libmount/src/utils.c b/libmount/src/utils.c +index 2151ff9..c13fb96 100644 +--- a/libmount/src/utils.c ++++ b/libmount/src/utils.c +@@ -269,6 +269,7 @@ int mnt_fstype_is_pseudofs(const char *type) + "mqueue", + "nfsd", + "none", ++ "overlay", + "pipefs", + "proc", + "pstore", +-- +2.7.4 + diff --git a/SOURCES/0074-logger-be-more-precise-about-port-description.patch b/SOURCES/0074-logger-be-more-precise-about-port-description.patch new file mode 100644 index 0000000..8ff9c46 --- /dev/null +++ b/SOURCES/0074-logger-be-more-precise-about-port-description.patch @@ -0,0 +1,41 @@ +From f4ffe3de3ba721892af52c7ed5787f4470393242 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 21 Jun 2016 13:35:33 +0200 +Subject: [PATCH 74/84] logger: be more precise about --port description + +Upstream: http://github.com/karelzak/util-linux/commit/1c7227598824b1d9140298e9fe5742cae4131130 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1344222 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + misc-utils/logger.1 | 1 + + misc-utils/logger.c | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/misc-utils/logger.1 b/misc-utils/logger.1 +index 5eb8d09..8c4faca 100644 +--- a/misc-utils/logger.1 ++++ b/misc-utils/logger.1 +@@ -72,6 +72,7 @@ port defined in /etc/services, which is often + \fB\-P\fR, \fB\-\-port\fR \fIport\fR + Use the specified + .IR port . ++When this option is not specified, the port defaults to syslog for udp and to syslog-conn for tcp connections. + .TP + \fB\-i\fR, \fB\-\-id\fR + Log the process ID of the logger process with each line. +diff --git a/misc-utils/logger.c b/misc-utils/logger.c +index c83c0b8..a331869 100644 +--- a/misc-utils/logger.c ++++ b/misc-utils/logger.c +@@ -222,7 +222,7 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out) + " -f, --file <file> log the contents of this file\n" + " -h, --help display this help text and exit\n"), out); + fputs(_(" -n, --server <name> write to this remote syslog server\n" +- " -P, --port <number> use this UDP port\n" ++ " -P, --port <port> use this port for UDP or TCP connection\n" + " -p, --priority <prio> mark given message with this priority\n" + " -s, --stderr output message to standard error as well\n"), out); + fputs(_(" -t, --tag <tag> mark every line with this tag\n" +-- +2.7.4 + diff --git a/SOURCES/0075-libfdisk-gpt-be-more-careful-with-64bit-constants.patch b/SOURCES/0075-libfdisk-gpt-be-more-careful-with-64bit-constants.patch new file mode 100644 index 0000000..82190fa --- /dev/null +++ b/SOURCES/0075-libfdisk-gpt-be-more-careful-with-64bit-constants.patch @@ -0,0 +1,182 @@ +From 428be59e33d0875cdf5bf602a75328fb3d7c58ad Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 21 Jun 2016 14:06:14 +0200 +Subject: [PATCH 75/84] libfdisk: (gpt) be more careful with 64bit constants + +It's probably more robust (and readable) to be explicit when we count +with constant and 64bit numbers. + +Upstream: http://github.com/karelzak/util-linux/commit/0a7cdf80606cc0670ef7f740d37640b05932e0ce +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1344482 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libfdisk/src/gpt.c | 40 ++++++++++++++++++++-------------------- + 1 file changed, 20 insertions(+), 20 deletions(-) + +diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c +index 482d453..d3bdc2d 100644 +--- a/libfdisk/src/gpt.c ++++ b/libfdisk/src/gpt.c +@@ -53,7 +53,7 @@ + #define GPT_MBR_PROTECTIVE 1 + #define GPT_MBR_HYBRID 2 + +-#define GPT_PRIMARY_PARTITION_TABLE_LBA 0x00000001 ++#define GPT_PRIMARY_PARTITION_TABLE_LBA 0x00000001ULL + + #define EFI_PMBR_OSTYPE 0xEE + #define MSDOS_MBR_SIGNATURE 0xAA55 +@@ -364,7 +364,7 @@ static int gpt_mknew_pmbr(struct fdisk_context *cxt) + pmbr->partition_record[0].end_track = 0xFF; + pmbr->partition_record[0].starting_lba = cpu_to_le32(1); + pmbr->partition_record[0].size_in_lba = +- cpu_to_le32(min((uint32_t) cxt->total_sectors - 1, 0xFFFFFFFF)); ++ cpu_to_le32((uint32_t) min(cxt->total_sectors - 1ULL, 0xFFFFFFFFULL)); + + return 0; + } +@@ -379,14 +379,14 @@ static void gpt_mknew_header_common(struct fdisk_context *cxt, + header->my_lba = cpu_to_le64(lba); + + if (lba == GPT_PRIMARY_PARTITION_TABLE_LBA) { /* primary */ +- header->alternative_lba = cpu_to_le64(cxt->total_sectors - 1); +- header->partition_entry_lba = cpu_to_le64(2); ++ header->alternative_lba = cpu_to_le64(cxt->total_sectors - 1ULL); ++ header->partition_entry_lba = cpu_to_le64(2ULL); + } else { /* backup */ + uint64_t esz = le32_to_cpu(header->npartition_entries) * sizeof(struct gpt_entry); + uint64_t esects = (esz + cxt->sector_size - 1) / cxt->sector_size; + + header->alternative_lba = cpu_to_le64(GPT_PRIMARY_PARTITION_TABLE_LBA); +- header->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1 - esects); ++ header->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1ULL - esects); + } + } + +@@ -451,8 +451,8 @@ static int gpt_mknew_header(struct fdisk_context *cxt, + header->npartition_entries = cpu_to_le32(GPT_NPARTITIONS); + header->sizeof_partition_entry = cpu_to_le32(sizeof(struct gpt_entry)); + +- last = cxt->total_sectors - 2 - esz; +- first = esz + 2; ++ last = cxt->total_sectors - 2ULL - esz; ++ first = esz + 2ULL; + + if (first < cxt->first_lba && cxt->first_lba < last) + /* Align according to topology */ +@@ -520,7 +520,7 @@ check_hybrid: + */ + if (ret == GPT_MBR_PROTECTIVE) { + if (le32_to_cpu(pmbr->partition_record[0].size_in_lba) != +- min((uint32_t) cxt->total_sectors - 1, 0xFFFFFFFF)) ++ (uint32_t) min(cxt->total_sectors - 1ULL, 0xFFFFFFFFULL)) + ret = 0; + } + done: +@@ -538,7 +538,7 @@ static uint64_t last_lba(struct fdisk_context *cxt) + } + + if (S_ISBLK(s.st_mode)) +- return cxt->total_sectors - 1; ++ return cxt->total_sectors - 1ULL; + else if (S_ISREG(s.st_mode)) { + uint64_t sectors = s.st_size >> cxt->sector_size; + return (sectors / cxt->sector_size) - 1ULL; +@@ -554,7 +554,7 @@ static ssize_t read_lba(struct fdisk_context *cxt, uint64_t lba, + + if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1) + return -1; +- return read(cxt->dev_fd, buffer, bytes) != bytes; ++ return read(cxt->dev_fd, buffer, bytes) != (ssize_t) bytes; + } + + +@@ -908,7 +908,7 @@ static uint64_t find_first_available(struct gpt_header *header, + if (first < gpt_partition_start(&e[i])) + continue; + if (first <= gpt_partition_end(&e[i])) { +- first = gpt_partition_end(&e[i]) + 1; ++ first = gpt_partition_end(&e[i]) + 1ULL; + first_moved = 1; + } + } +@@ -937,7 +937,7 @@ static uint64_t find_last_free(struct gpt_header *header, + uint64_t ps = gpt_partition_start(&e[i]); + + if (nearest_start > ps && ps > start) +- nearest_start = ps - 1; ++ nearest_start = ps - 1ULL; + } + + return nearest_start; +@@ -960,7 +960,7 @@ static uint64_t find_last_free_sector(struct gpt_header *header, + for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) { + if ((last >= gpt_partition_start(&e[i])) && + (last <= gpt_partition_end(&e[i]))) { +- last = gpt_partition_start(&e[i]) - 1; ++ last = gpt_partition_start(&e[i]) - 1ULL; + last_moved = 1; + } + } +@@ -986,7 +986,7 @@ static uint64_t find_first_in_largest(struct gpt_header *header, struct gpt_entr + first_sect = find_first_available(header, e, start); + if (first_sect != 0) { + last_sect = find_last_free(header, e, first_sect); +- segment_size = last_sect - first_sect + 1; ++ segment_size = last_sect - first_sect + 1ULL; + + if (segment_size > selected_size) { + selected_size = segment_size; +@@ -1026,7 +1026,7 @@ static uint64_t get_free_sectors(struct fdisk_context *cxt, struct gpt_header *h + largest_seg = segment_sz; + totfound += segment_sz; + num++; +- start = last_sect + 1; ++ start = last_sect + 1ULL; + } + } while (first_sect); + +@@ -1165,7 +1165,7 @@ void gpt_list_table(struct fdisk_context *cxt, + continue; + + /* the partition has to inside usable range */ +- if (start < fu || start + size - 1 > lu) ++ if (start < fu || start + size - 1ULL > lu) + continue; + + name = encode_to_utf8((unsigned char *)gpt->ents[i].partition_name, +@@ -1266,11 +1266,11 @@ static int gpt_write_pmbr(struct fdisk_context *cxt) + * Set size_in_lba to the size of the disk minus one. If the size of the disk + * is too large to be represented by a 32bit LBA (2Tb), set it to 0xFFFFFFFF. + */ +- if (cxt->total_sectors - 1 > 0xFFFFFFFFULL) ++ if (cxt->total_sectors - 1ULL > 0xFFFFFFFFULL) + pmbr->partition_record[0].size_in_lba = cpu_to_le32(0xFFFFFFFF); + else + pmbr->partition_record[0].size_in_lba = +- cpu_to_le32(cxt->total_sectors - 1UL); ++ cpu_to_le32((uint32_t) (cxt->total_sectors - 1ULL)); + + offset = GPT_PMBR_LBA * cxt->sector_size; + if (offset != lseek(cxt->dev_fd, offset, SEEK_SET)) +@@ -1308,7 +1308,7 @@ static int gpt_write_disklabel(struct fdisk_context *cxt) + goto err0; + + /* check that the backup header is properly placed */ +- if (le64_to_cpu(gpt->pheader->alternative_lba) < cxt->total_sectors - 1) ++ if (le64_to_cpu(gpt->pheader->alternative_lba) < cxt->total_sectors - 1ULL) + /* TODO: correct this (with user authorization) and write */ + goto err0; + +@@ -1645,7 +1645,7 @@ static int gpt_add_partition( + + user_l = fdisk_ask_number_get_result(ask); + if (fdisk_ask_number_is_relative(ask)) +- user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1; ++ user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1ULL; + if (user_l > user_f && user_l <= disk_l) + break; + } +-- +2.7.4 + diff --git a/SOURCES/0076-lsns-backport-new-command.patch b/SOURCES/0076-lsns-backport-new-command.patch new file mode 100644 index 0000000..090bd16 --- /dev/null +++ b/SOURCES/0076-lsns-backport-new-command.patch @@ -0,0 +1,1251 @@ +From 03f539c766780a083010636cc67f96fcb2bab30f Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 24 Jun 2016 12:16:24 +0200 +Subject: [PATCH 76/84] lsns: backport new command + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1332084 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + bash-completion/lsns | 55 ++++ + configure.ac | 6 + + include/Makemodule.am | 1 + + include/c.h | 8 + + include/debug.h | 2 +- + include/idcache.h | 28 ++ + include/procutils.h | 2 + + lib/Makemodule.am | 1 + + lib/idcache.c | 117 ++++++++ + lib/procutils.c | 43 +++ + sys-utils/Makemodule.am | 7 + + sys-utils/lsns.8 | 78 +++++ + sys-utils/lsns.c | 748 ++++++++++++++++++++++++++++++++++++++++++++++++ + 13 files changed, 1095 insertions(+), 1 deletion(-) + create mode 100644 bash-completion/lsns + create mode 100644 include/idcache.h + create mode 100644 lib/idcache.c + create mode 100644 sys-utils/lsns.8 + create mode 100644 sys-utils/lsns.c + +diff --git a/bash-completion/lsns b/bash-completion/lsns +new file mode 100644 +index 0000000..d02df3b +--- /dev/null ++++ b/bash-completion/lsns +@@ -0,0 +1,55 @@ ++_lsns_module() ++{ ++ local cur prev OPTS LSNS_COLS_ALL ++ COMPREPLY=() ++ cur="${COMP_WORDS[COMP_CWORD]}" ++ prev="${COMP_WORDS[COMP_CWORD-1]}" ++ LSNS_COLS_ALL=" ++ NS TYPE PATH NPROCS PID PPID COMMAND UID USER ++ " ++ case $prev in ++ '-o'|'--output') ++ local prefix realcur LSNS_COLS ++ realcur="${cur##*,}" ++ prefix="${cur%$realcur}" ++ for WORD in $LSNS_COLS_ALL; do ++ if ! [[ $prefix == *"$WORD"* ]]; then ++ LSNS_COLS="$WORD $LSNS_COLS" ++ fi ++ done ++ compopt -o nospace ++ COMPREPLY=( $(compgen -P "$prefix" -W "$LSNS_COLS" -S ',' -- $realcur) ) ++ return 0 ++ ;; ++ '-p'|'--task') ++ COMPREPLY=( $(compgen -W "$(cd /proc && echo [0-9]*)" -- $cur) ) ++ return 0 ++ ;; ++ '-t'|'--type') ++ COMPREPLY=( $(compgen -W "mnt net ipc user pid uts" -- $cur) ) ++ return 0 ++ ;; ++ '-h'|'--help'|'-V'|'--version') ++ return 0 ++ ;; ++ esac ++ case $cur in ++ -*) ++ COMPREPLY=( $(compgen -W " ++ --list ++ --noheadings ++ --output ++ --task ++ --raw ++ --notruncate ++ --type ++ --help ++ --version ++ " -- $cur) ) ++ return 0 ++ ;; ++ esac ++ COMPREPLY=( $(compgen -W "mnt net pid uts ipc user" -- $cur ) ) ++ return 0 ++} ++complete -F _lsns_module lsns +diff --git a/configure.ac b/configure.ac +index f3c7214..5d9ea39 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1032,6 +1032,12 @@ UL_REQUIRES_BUILD([lslogins], [libsmartcols]) + AM_CONDITIONAL([BUILD_LSLOGINS], [test "x$build_lslogins" = xyes]) + + ++UL_BUILD_INIT([lsns], [check]) ++UL_REQUIRES_LINUX([lsns]) ++UL_REQUIRES_BUILD([lsns], [libsmartcols]) ++AM_CONDITIONAL([BUILD_LSNS], [test "x$build_lsns" = xyes]) ++ ++ + UL_BUILD_INIT([chcpu], [check]) + UL_REQUIRES_LINUX([chcpu]) + UL_REQUIRES_HAVE([chcpu], [cpu_set_t], [cpu_set_t type]) +diff --git a/include/Makemodule.am b/include/Makemodule.am +index 7b53244..757f317 100644 +--- a/include/Makemodule.am ++++ b/include/Makemodule.am +@@ -15,6 +15,7 @@ dist_noinst_HEADERS += \ + include/exec_shell.h \ + include/exitcodes.h \ + include/fileutils.h \ ++ include/idcache.h \ + include/ismounted.h \ + include/linux_reboot.h \ + include/linux_version.h \ +diff --git a/include/c.h b/include/c.h +index 7b59ce8..a2779a5 100644 +--- a/include/c.h ++++ b/include/c.h +@@ -110,6 +110,14 @@ + _max1 > _max2 ? _max1 : _max2; }) + #endif + ++#ifndef cmp_numbers ++# define cmp_numbers(x, y) __extension__ ({ \ ++ __typeof__(x) _a = (x); \ ++ __typeof__(y) _b = (y); \ ++ (void) (&_a == &_b); \ ++ _a == _b ? 0 : _a > _b ? 1 : -1; }) ++#endif ++ + #ifndef offsetof + #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + #endif +diff --git a/include/debug.h b/include/debug.h +index 25045aa..848e474 100644 +--- a/include/debug.h ++++ b/include/debug.h +@@ -15,7 +15,7 @@ struct dbg_mask { char *mname; int val; }; + + #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[] ++#define UL_DEBUG_DEFINE_MASKNAMES(m) static const struct dbg_mask m ## _masknames[] + + /* p - flag prefix, m - flag postfix */ + #define UL_DEBUG_DEFINE_FLAG(p, m) p ## m +diff --git a/include/idcache.h b/include/idcache.h +new file mode 100644 +index 0000000..912edd5 +--- /dev/null ++++ b/include/idcache.h +@@ -0,0 +1,28 @@ ++#ifndef UTIL_LINUX_IDCACHE_H ++#define UTIL_LINUX_IDCACHE_H ++ ++#include <sys/types.h> ++#include <pwd.h> ++ ++#define IDCACHE_FLAGS_NAMELEN (1 << 1) ++ ++struct identry { ++ unsigned long int id; ++ char *name; ++ struct identry *next; ++}; ++ ++struct idcache { ++ struct identry *ent; /* first entry */ ++ int width; /* name width */ ++}; ++ ++ ++extern struct idcache *new_idcache(void); ++extern void add_gid(struct idcache *cache, unsigned long int id); ++extern void add_uid(struct idcache *cache, unsigned long int id); ++ ++extern void free_idcache(struct idcache *ic); ++extern struct identry *get_id(struct idcache *ic, unsigned long int id); ++ ++#endif /* UTIL_LINUX_IDCACHE_H */ +diff --git a/include/procutils.h b/include/procutils.h +index 14b766c..9f8dd76 100644 +--- a/include/procutils.h ++++ b/include/procutils.h +@@ -28,5 +28,7 @@ extern void proc_processes_filter_by_name(struct proc_processes *ps, const char + extern void proc_processes_filter_by_uid(struct proc_processes *ps, uid_t uid); + extern int proc_next_pid(struct proc_processes *ps, pid_t *pid); + ++extern char *proc_get_command(pid_t pid); ++extern char *proc_get_command_name(pid_t pid); + + #endif /* UTIL_LINUX_PROCUTILS */ +diff --git a/lib/Makemodule.am b/lib/Makemodule.am +index eed31f1..73280f9 100644 +--- a/lib/Makemodule.am ++++ b/lib/Makemodule.am +@@ -8,6 +8,7 @@ libcommon_la_SOURCES = \ + lib/colors.c \ + lib/crc32.c \ + lib/env.c \ ++ lib/idcache.c \ + lib/fileutils.c \ + lib/ismounted.c \ + lib/mangle.c \ +diff --git a/lib/idcache.c b/lib/idcache.c +new file mode 100644 +index 0000000..3c358b8 +--- /dev/null ++++ b/lib/idcache.c +@@ -0,0 +1,117 @@ ++ ++#include <wchar.h> ++#include <pwd.h> ++#include <grp.h> ++#include <sys/types.h> ++ ++#include "c.h" ++#include "idcache.h" ++ ++#ifndef LOGIN_NAME_MAX ++#define LOGIN_NAME_MAX 256 ++#endif ++ ++struct identry *get_id(struct idcache *ic, unsigned long int id) ++{ ++ struct identry *ent; ++ ++ if (!ic) ++ return NULL; ++ ++ for (ent = ic->ent; ent; ent = ent->next) { ++ if (ent->id == id) ++ return ent; ++ } ++ ++ return NULL; ++} ++ ++struct idcache *new_idcache() ++{ ++ return calloc(1, sizeof(struct idcache)); ++} ++ ++void free_idcache(struct idcache *ic) ++{ ++ struct identry *ent = ic->ent; ++ ++ while (ent) { ++ struct identry *next = ent->next; ++ free(ent->name); ++ free(ent); ++ ent = next; ++ } ++ ++ free(ic); ++} ++ ++static void add_id(struct idcache *ic, char *name, unsigned long int id) ++{ ++ struct identry *ent, *x; ++ int w = 0; ++ ++ ent = calloc(1, sizeof(struct identry)); ++ if (!ent) ++ return; ++ ent->id = id; ++ ++ if (name) { ++#ifdef HAVE_WIDECHAR ++ wchar_t wc[LOGIN_NAME_MAX + 1]; ++ ++ if (mbstowcs(wc, name, LOGIN_NAME_MAX) > 0) { ++ wc[LOGIN_NAME_MAX] = '\0'; ++ w = wcswidth(wc, LOGIN_NAME_MAX); ++ } ++ else ++#endif ++ w = strlen(name); ++ } ++ ++ /* note, we ignore names with non-printable widechars */ ++ if (w > 0) { ++ ent->name = strdup(name); ++ if (!ent->name) { ++ free(ent); ++ return; ++ } ++ } else { ++ if (asprintf(&ent->name, "%lu", id) < 0) { ++ free(ent); ++ return; ++ } ++ } ++ ++ for (x = ic->ent; x && x->next; x = x->next); ++ ++ if (x) ++ x->next = ent; ++ else ++ ic->ent = ent; ++ ++ if (w <= 0) ++ w = ent->name ? strlen(ent->name) : 0; ++ ic->width = ic->width < w ? w : ic->width; ++ return; ++} ++ ++void add_uid(struct idcache *cache, unsigned long int id) ++{ ++ struct identry *ent= get_id(cache, id); ++ ++ if (!ent) { ++ struct passwd *pw = getpwuid((uid_t) id); ++ add_id(cache, pw ? pw->pw_name : NULL, id); ++ } ++} ++ ++void add_gid(struct idcache *cache, unsigned long int id) ++{ ++ struct identry *ent = get_id(cache, id); ++ ++ if (!ent) { ++ struct group *gr = getgrgid((gid_t) id); ++ add_id(cache, gr ? gr->gr_name : NULL, id); ++ } ++} ++ +diff --git a/lib/procutils.c b/lib/procutils.c +index d633261..8dfdec9 100644 +--- a/lib/procutils.c ++++ b/lib/procutils.c +@@ -25,6 +25,7 @@ + #include "procutils.h" + #include "at.h" + #include "c.h" ++#include "all-io.h" + + /* + * @pid: process ID for which we want to obtain the threads group +@@ -193,6 +194,48 @@ int proc_next_pid(struct proc_processes *ps, pid_t *pid) + return 0; + } + ++/* returns process command path, use free() for result */ ++static char *proc_file_strdup(pid_t pid, const char *name) ++{ ++ char buf[BUFSIZ], *res = NULL; ++ ssize_t sz = 0; ++ size_t i; ++ int fd; ++ ++ snprintf(buf, sizeof(buf), "/proc/%d/%s", (int) pid, name); ++ fd = open(buf, O_RDONLY); ++ if (fd < 0) ++ goto done; ++ ++ sz = read_all(fd, buf, sizeof(buf)); ++ if (sz <= 0) ++ goto done; ++ ++ for (i = 0; i < (size_t) sz; i++) { ++ ++ if (buf[i] == '\0') ++ buf[i] = ' '; ++ } ++ buf[sz - 1] = '\0'; ++ res = strdup(buf); ++done: ++ if (fd >= 0) ++ close(fd); ++ return res; ++} ++ ++/* returns process command path, use free() for result */ ++char *proc_get_command(pid_t pid) ++{ ++ return proc_file_strdup(pid, "cmdline"); ++} ++ ++/* returns process command name, use free() for result */ ++char *proc_get_command_name(pid_t pid) ++{ ++ return proc_file_strdup(pid, "comm"); ++} ++ + #ifdef TEST_PROGRAM + + static int test_tasks(int argc, char *argv[]) +diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am +index c6c561c..9baf5a3 100644 +--- a/sys-utils/Makemodule.am ++++ b/sys-utils/Makemodule.am +@@ -182,6 +182,13 @@ prlimit_SOURCES = sys-utils/prlimit.c + prlimit_LDADD = $(LDADD) libcommon.la + endif + ++if BUILD_LSNS ++usrbin_exec_PROGRAMS += lsns ++dist_man_MANS += sys-utils/lsns.8 ++lsns_SOURCES = sys-utils/lsns.c ++lsns_LDADD = $(LDADD) libcommon.la libsmartcols.la ++lsns_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) ++endif + + if BUILD_MOUNT + # +diff --git a/sys-utils/lsns.8 b/sys-utils/lsns.8 +new file mode 100644 +index 0000000..328df47 +--- /dev/null ++++ b/sys-utils/lsns.8 +@@ -0,0 +1,78 @@ ++.\" Man page for the lsns command. ++.\" Copyright 2015 Karel Zak <kzak@redhat.com> ++.\" May be distributed under the GNU General Public License ++ ++.TH LSNS 8 "December 2015" "util-linux" "System Administration" ++.SH NAME ++lsns \- list namespaces ++.SH SYNOPSIS ++.B lsns ++[options] ++.RI [ namespace ] ++ ++.SH DESCRIPTION ++.B lsns ++lists information about all the currently accessible namespaces or about the ++given \fInamespace\fP. The \fInamespace\fP identifier is an inode number. ++ ++The default output is subject to change. So whenever possible, you should ++avoid using default outputs in your scripts. Always explicitly define expected ++columns by using the \fB\-\-output\fR option together with a columns list in ++environments where a stable output is required. ++ ++Note that \fBlsns\fR reads information directly from the /proc filesystem and ++for non-root users it may return incomplete information. The current /proc ++filesystem may be unshared and affected by a PID namespace ++(see \fBunshare \-\-mount\-proc\fP for more details). ++.B lsns ++is not able to see persistent namespaces without processes where the namespace ++instance is held by a bind mount to /proc/\fIpid\fR/ns/\fItype\fR. ++ ++.SH OPTIONS ++.TP ++.BR \-l , " \-\-list" ++Use list output format. ++.TP ++.BR \-n , " \-\-noheadings" ++Do not print a header line. ++.TP ++.BR \-o , " \-\-output " \fIlist\fP ++Specify which output columns to print. Use \fB\-\-help\fR ++to get a list of all supported columns. ++ ++The default list of columns may be extended if \fIlist\fP is ++specified in the format \fB+\fIlist\fP (e.g. \fBlsns \-o +PATH\fP). ++.TP ++.BR \-p , " \-\-task " \fIpid\fP ++Display only the namespaces held by the process with this \fIpid\fR. ++.TP ++.BR \-r , " \-\-raw" ++Use the raw output format. ++.TP ++.BR \-t , " \-\-type " \fItype\fP ++Display the specified \fItype\fP of namespaces only. The supported types are ++\fBmnt\fP, \fBnet\fP, \fBipc\fP, \fBuser\fP, \fBpid\fP and \fButs\fP. This ++option may be given more than once. ++.TP ++.BR \-u , " \-\-notruncate" ++Do not truncate text in columns. ++.TP ++.BR \-V , " \-\-version" ++Display version information and exit. ++.TP ++.BR \-h , " \-\-help" ++Display help text and exit. ++ ++.SH AUTHORS ++.nf ++Karel Zak <kzak@redhat.com> ++.fi ++ ++.SH "SEE ALSO" ++.BR unshare (1), ++.BR nsenter (1), ++.BR clone (2) ++ ++.SH AVAILABILITY ++The lsns command is part of the util-linux package and is available from ++ftp://ftp.kernel.org/pub/linux/utils/util-linux/. +diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c +new file mode 100644 +index 0000000..5ee2981 +--- /dev/null ++++ b/sys-utils/lsns.c +@@ -0,0 +1,748 @@ ++/* ++ * lsns(8) - list system namespaces ++ * ++ * Copyright (C) 2015 Karel Zak <kzak@redhat.com> ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include <stdio.h> ++#include <string.h> ++#include <getopt.h> ++#include <stdlib.h> ++#include <assert.h> ++#include <dirent.h> ++#include <unistd.h> ++#include <sys/stat.h> ++#include <sys/types.h> ++#include <wchar.h> ++#include <libsmartcols.h> ++ ++#include "pathnames.h" ++#include "nls.h" ++#include "xalloc.h" ++#include "c.h" ++#include "list.h" ++#include "closestream.h" ++#include "optutils.h" ++#include "procutils.h" ++#include "strutils.h" ++#include "namespace.h" ++#include "path.h" ++#include "idcache.h" ++ ++#include "debug.h" ++ ++UL_DEBUG_DEFINE_MASK(lsns); ++UL_DEBUG_DEFINE_MASKNAMES(lsns) = UL_DEBUG_EMPTY_MASKNAMES; ++ ++#define LSNS_DEBUG_INIT (1 << 1) ++#define LSNS_DEBUG_PROC (1 << 2) ++#define LSNS_DEBUG_NS (1 << 3) ++#define LSNS_DEBUG_ALL 0xFFFF ++ ++#define DBG(m, x) __UL_DBG(lsns, LSNS_DEBUG_, m, x) ++#define ON_DBG(m, x) __UL_DBG_CALL(lsns, LSNS_DEBUG_, m, x) ++ ++struct idcache *uid_cache = NULL; ++ ++/* column IDs */ ++enum { ++ COL_NS = 0, ++ COL_TYPE, ++ COL_PATH, ++ COL_NPROCS, ++ COL_PID, ++ COL_PPID, ++ COL_COMMAND, ++ COL_UID, ++ COL_USER ++}; ++ ++/* column names */ ++struct colinfo { ++ const char *name; /* header */ ++ double whint; /* width hint (N < 1 is in percent of termwidth) */ ++ int flags; /* SCOLS_FL_* */ ++ const char *help; ++}; ++ ++/* columns descriptions */ ++static const struct colinfo infos[] = { ++ [COL_NS] = { "NS", 10, SCOLS_FL_RIGHT, N_("namespace identifier (inode number)") }, ++ [COL_TYPE] = { "TYPE", 5, 0, N_("kind of namespace") }, ++ [COL_PATH] = { "PATH", 0, 0, N_("path to the namespace")}, ++ [COL_NPROCS] = { "NPROCS", 5, SCOLS_FL_RIGHT, N_("number of processes in the namespace") }, ++ [COL_PID] = { "PID", 5, SCOLS_FL_RIGHT, N_("lowest PID in the namespace") }, ++ [COL_PPID] = { "PPID", 5, SCOLS_FL_RIGHT, N_("PPID of the PID") }, ++ [COL_COMMAND] = { "COMMAND", 0, SCOLS_FL_TRUNC, N_("command line of the PID")}, ++ [COL_UID] = { "UID", 0, SCOLS_FL_RIGHT, N_("UID of the PID")}, ++ [COL_USER] = { "USER", 0, 0, N_("username of the PID")} ++}; ++ ++static int columns[ARRAY_SIZE(infos) * 2]; ++static size_t ncolumns; ++ ++enum { ++ LSNS_ID_MNT = 0, ++ LSNS_ID_NET, ++ LSNS_ID_PID, ++ LSNS_ID_UTS, ++ LSNS_ID_IPC, ++ LSNS_ID_USER ++}; ++ ++static char *ns_names[] = { ++ [LSNS_ID_MNT] = "mnt", ++ [LSNS_ID_NET] = "net", ++ [LSNS_ID_PID] = "pid", ++ [LSNS_ID_UTS] = "uts", ++ [LSNS_ID_IPC] = "ipc", ++ [LSNS_ID_USER] = "user" ++}; ++ ++struct lsns_namespace { ++ ino_t id; ++ int type; /* LSNS_* */ ++ int nprocs; ++ ++ struct lsns_process *proc; ++ ++ struct list_head namespaces; /* lsns->processes member */ ++ struct list_head processes; /* head of lsns_process *siblings */ ++}; ++ ++struct lsns_process { ++ pid_t pid; /* process PID */ ++ pid_t ppid; /* parent's PID */ ++ pid_t tpid; /* thread group */ ++ char state; ++ uid_t uid; ++ ++ ino_t ns_ids[ARRAY_SIZE(ns_names)]; ++ struct list_head ns_siblings[ARRAY_SIZE(ns_names)]; ++ ++ struct list_head processes; /* list of processes */ ++ ++ struct libscols_line *outline; ++ struct lsns_process *parent; ++}; ++ ++struct lsns { ++ struct list_head processes; ++ struct list_head namespaces; ++ ++ pid_t fltr_pid; /* filter out by PID */ ++ ino_t fltr_ns; /* filter out by namespace */ ++ int fltr_types[ARRAY_SIZE(ns_names)]; ++ int fltr_ntypes; ++ ++ unsigned int raw : 1, ++ tree : 1, ++ list : 1, ++ notrunc : 1, ++ no_headings: 1; ++}; ++ ++static void lsns_init_debug(void) ++{ ++ __UL_INIT_DEBUG(lsns, LSNS_DEBUG_, 0, LSNS_DEBUG); ++} ++ ++static int ns_name2type(const char *name) ++{ ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(ns_names); i++) { ++ if (strcmp(ns_names[i], name) == 0) ++ return i; ++ } ++ return -1; ++} ++ ++static int column_name_to_id(const char *name, size_t namesz) ++{ ++ size_t i; ++ ++ assert(name); ++ ++ for (i = 0; i < ARRAY_SIZE(infos); i++) { ++ const char *cn = infos[i].name; ++ ++ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) ++ return i; ++ } ++ warnx(_("unknown column: %s"), name); ++ return -1; ++} ++ ++static inline int get_column_id(int num) ++{ ++ assert(num >= 0); ++ assert((size_t) num < ncolumns); ++ assert(columns[num] < (int) ARRAY_SIZE(infos)); ++ ++ return columns[num]; ++} ++ ++static inline const struct colinfo *get_column_info(unsigned num) ++{ ++ return &infos[ get_column_id(num) ]; ++} ++ ++static ino_t get_ns_ino(int dir, const char *nsname, ino_t *ino) ++{ ++ struct stat st; ++ char path[16]; ++ ++ snprintf(path, sizeof(path), "ns/%s", nsname); ++ ++ if (fstatat(dir, path, &st, 0) != 0) ++ return -errno; ++ *ino = st.st_ino; ++ return 0; ++} ++ ++ ++static int read_process(struct lsns *ls, pid_t pid) ++{ ++ struct lsns_process *p = NULL; ++ char buf[BUFSIZ]; ++ DIR *dir; ++ int rc = 0, fd; ++ FILE *f = NULL; ++ size_t i; ++ struct stat st; ++ ++ DBG(PROC, ul_debug("reading %d", (int) pid)); ++ ++ snprintf(buf, sizeof(buf), "/proc/%d", pid); ++ dir = opendir(buf); ++ if (!dir) ++ return -errno; ++ ++ p = xcalloc(1, sizeof(*p)); ++ if (!p) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ if (fstat(dirfd(dir), &st) == 0) { ++ p->uid = st.st_uid; ++ add_uid(uid_cache, st.st_uid); ++ } ++ ++ fd = openat(dirfd(dir), "stat", O_RDONLY); ++ if (fd < 0) { ++ rc = -errno; ++ goto done; ++ } ++ if (!(f = fdopen(fd, "r"))) { ++ rc = -errno; ++ goto done; ++ } ++ rc = fscanf(f, "%d %*s %c %d*[^\n]", &p->pid, &p->state, &p->ppid); ++ if (rc != 3) { ++ rc = rc < 0 ? -errno : -EINVAL; ++ goto done; ++ } ++ rc = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(p->ns_ids); i++) { ++ INIT_LIST_HEAD(&p->ns_siblings[i]); ++ ++ if (!ls->fltr_types[i]) ++ continue; ++ ++ rc = get_ns_ino(dirfd(dir), ns_names[i], &p->ns_ids[i]); ++ if (rc && rc != -EACCES) ++ goto done; ++ rc = 0; ++ } ++ ++ INIT_LIST_HEAD(&p->processes); ++ ++ DBG(PROC, ul_debugobj(p, "new pid=%d", p->pid)); ++ list_add_tail(&p->processes, &ls->processes); ++done: ++ if (f) ++ fclose(f); ++ closedir(dir); ++ if (rc) ++ free(p); ++ return rc; ++} ++ ++static int read_processes(struct lsns *ls) ++{ ++ struct proc_processes *proc = NULL; ++ pid_t pid; ++ int rc = 0; ++ ++ DBG(PROC, ul_debug("opening /proc")); ++ ++ if (!(proc = proc_open_processes())) { ++ rc = -errno; ++ goto done; ++ } ++ ++ while (proc_next_pid(proc, &pid) == 0) { ++ rc = read_process(ls, pid); ++ if (rc && rc != -EACCES && rc != -ENOENT) ++ break; ++ rc = 0; ++ } ++done: ++ DBG(PROC, ul_debug("closing /proc")); ++ proc_close_processes(proc); ++ return rc; ++} ++ ++static struct lsns_namespace *get_namespace(struct lsns *ls, ino_t ino) ++{ ++ struct list_head *p; ++ ++ list_for_each(p, &ls->namespaces) { ++ struct lsns_namespace *ns = list_entry(p, struct lsns_namespace, namespaces); ++ ++ if (ns->id == ino) ++ return ns; ++ } ++ return NULL; ++} ++ ++static int namespace_has_process(struct lsns_namespace *ns, pid_t pid) ++{ ++ struct list_head *p; ++ ++ list_for_each(p, &ns->processes) { ++ struct lsns_process *proc = list_entry(p, struct lsns_process, ns_siblings[ns->type]); ++ ++ if (proc->pid == pid) ++ return 1; ++ } ++ return 0; ++} ++ ++static struct lsns_namespace *add_namespace(struct lsns *ls, int type, ino_t ino) ++{ ++ struct lsns_namespace *ns = xcalloc(1, sizeof(*ns)); ++ ++ if (!ns) ++ return NULL; ++ ++ DBG(NS, ul_debugobj(ns, "new %s[%ju]", ns_names[type], (uintmax_t)ino)); ++ ++ INIT_LIST_HEAD(&ns->processes); ++ INIT_LIST_HEAD(&ns->namespaces); ++ ++ ns->type = type; ++ ns->id = ino; ++ ++ list_add_tail(&ns->namespaces, &ls->namespaces); ++ return ns; ++} ++ ++static int add_process_to_namespace(struct lsns *ls, struct lsns_namespace *ns, struct lsns_process *proc) ++{ ++ struct list_head *p; ++ ++ DBG(NS, ul_debugobj(ns, "add process [%p] pid=%d to %s[%ju]", ++ proc, proc->pid, ns_names[ns->type], (uintmax_t)ns->id)); ++ ++ list_for_each(p, &ls->processes) { ++ struct lsns_process *xproc = list_entry(p, struct lsns_process, processes); ++ ++ if (xproc->pid == proc->ppid) /* my parent */ ++ proc->parent = xproc; ++ else if (xproc->ppid == proc->pid) /* my child */ ++ xproc->parent = proc; ++ } ++ ++ list_add_tail(&proc->ns_siblings[ns->type], &ns->processes); ++ ns->nprocs++; ++ ++ if (!ns->proc || ns->proc->pid > proc->pid) ++ ns->proc = proc; ++ ++ return 0; ++} ++ ++static int cmp_namespaces(struct list_head *a, struct list_head *b, ++ __attribute__((__unused__)) void *data) ++{ ++ struct lsns_namespace *xa = list_entry(a, struct lsns_namespace, namespaces), ++ *xb = list_entry(b, struct lsns_namespace, namespaces); ++ ++ return cmp_numbers(xa->id, xb->id); ++} ++ ++static int read_namespaces(struct lsns *ls) ++{ ++ struct list_head *p; ++ ++ DBG(NS, ul_debug("reading namespace")); ++ ++ list_for_each(p, &ls->processes) { ++ size_t i; ++ struct lsns_namespace *ns; ++ struct lsns_process *proc = list_entry(p, struct lsns_process, processes); ++ ++ for (i = 0; i < ARRAY_SIZE(proc->ns_ids); i++) { ++ if (proc->ns_ids[i] == 0) ++ continue; ++ if (!(ns = get_namespace(ls, proc->ns_ids[i]))) { ++ ns = add_namespace(ls, i, proc->ns_ids[i]); ++ if (!ns) ++ return -ENOMEM; ++ } ++ add_process_to_namespace(ls, ns, proc); ++ } ++ } ++ ++ list_sort(&ls->namespaces, cmp_namespaces, NULL); ++ ++ return 0; ++} ++ ++static void add_scols_line(struct lsns *ls, struct libscols_table *table, ++ struct lsns_namespace *ns, struct lsns_process *proc) ++{ ++ size_t i; ++ struct libscols_line *line; ++ ++ assert(ns); ++ assert(table); ++ ++ line = scols_table_new_line(table, ++ ls->tree && proc->parent ? proc->parent->outline : NULL); ++ if (!line) { ++ warn(_("failed to add line to output")); ++ return; ++ } ++ ++ for (i = 0; i < ncolumns; i++) { ++ char *str = NULL; ++ ++ switch (get_column_id(i)) { ++ case COL_NS: ++ xasprintf(&str, "%ju", (uintmax_t)ns->id); ++ break; ++ case COL_PID: ++ xasprintf(&str, "%d", (int) proc->pid); ++ break; ++ case COL_PPID: ++ xasprintf(&str, "%d", (int) proc->ppid); ++ break; ++ case COL_TYPE: ++ xasprintf(&str, "%s", ns_names[ns->type]); ++ break; ++ case COL_NPROCS: ++ xasprintf(&str, "%d", ns->nprocs); ++ break; ++ case COL_COMMAND: ++ str = proc_get_command(proc->pid); ++ if (!str) ++ str = proc_get_command_name(proc->pid); ++ break; ++ case COL_PATH: ++ xasprintf(&str, "/proc/%d/ns/%s", (int) proc->pid, ns_names[ns->type]); ++ break; ++ case COL_UID: ++ xasprintf(&str, "%d", (int) proc->uid); ++ break; ++ case COL_USER: ++ xasprintf(&str, "%s", get_id(uid_cache, proc->uid)->name); ++ break; ++ default: ++ break; ++ } ++ ++ if (str) ++ scols_line_set_data(line, i, str); ++ } ++ ++ proc->outline = line; ++} ++ ++static struct libscols_table *init_scols_table(struct lsns *ls) ++{ ++ struct libscols_table *tab; ++ size_t i; ++ ++ tab = scols_new_table(); ++ if (!tab) { ++ warn(_("failed to initialize output table")); ++ return NULL; ++ } ++ ++ scols_table_enable_raw(tab, ls->raw); ++ scols_table_enable_noheadings(tab, ls->no_headings); ++ ++ for (i = 0; i < ncolumns; i++) { ++ const struct colinfo *col = get_column_info(i); ++ int flags = col->flags; ++ ++ if (ls->notrunc) ++ flags &= ~SCOLS_FL_TRUNC; ++ if (ls->tree && get_column_id(i) == COL_COMMAND) ++ flags |= SCOLS_FL_TREE; ++ ++ if (!scols_table_new_column(tab, col->name, col->whint, flags)) { ++ warnx(_("failed to initialize output column")); ++ goto err; ++ } ++ } ++ ++ return tab; ++err: ++ scols_unref_table(tab); ++ return NULL; ++} ++ ++static int show_namespaces(struct lsns *ls) ++{ ++ struct libscols_table *tab; ++ struct list_head *p; ++ int rc = 0; ++ ++ tab = init_scols_table(ls); ++ if (!tab) ++ return -ENOMEM; ++ ++ list_for_each(p, &ls->namespaces) { ++ struct lsns_namespace *ns = list_entry(p, struct lsns_namespace, namespaces); ++ ++ if (ls->fltr_pid != 0 && !namespace_has_process(ns, ls->fltr_pid)) ++ continue; ++ ++ add_scols_line(ls, tab, ns, ns->proc); ++ } ++ ++ scols_print_table(tab); ++ scols_unref_table(tab); ++ return rc; ++} ++ ++static void show_process(struct lsns *ls, struct libscols_table *tab, ++ struct lsns_process *proc, struct lsns_namespace *ns) ++{ ++ /* ++ * create a tree from parent->child relation, but only if the parent is ++ * within the same namespace ++ */ ++ if (ls->tree ++ && proc->parent ++ && !proc->parent->outline ++ && proc->parent->ns_ids[ns->type] == proc->ns_ids[ns->type]) ++ show_process(ls, tab, proc->parent, ns); ++ ++ add_scols_line(ls, tab, ns, proc); ++} ++ ++ ++static int show_namespace_processes(struct lsns *ls, struct lsns_namespace *ns) ++{ ++ struct libscols_table *tab; ++ struct list_head *p; ++ ++ tab = init_scols_table(ls); ++ if (!tab) ++ return -ENOMEM; ++ ++ list_for_each(p, &ns->processes) { ++ struct lsns_process *proc = list_entry(p, struct lsns_process, ns_siblings[ns->type]); ++ ++ if (!proc->outline) ++ show_process(ls, tab, proc, ns); ++ } ++ ++ ++ scols_print_table(tab); ++ scols_unref_table(tab); ++ return 0; ++} ++ ++static void __attribute__ ((__noreturn__)) usage(FILE * out) ++{ ++ size_t i; ++ ++ fputs(USAGE_HEADER, out); ++ ++ fprintf(out, ++ _(" %s [options] [<namespace>]\n"), program_invocation_short_name); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("List system namespaces.\n"), out); ++ ++ fputs(USAGE_OPTIONS, out); ++ fputs(_(" -l, --list use list format output\n"), out); ++ fputs(_(" -n, --noheadings don't print headings\n"), out); ++ fputs(_(" -o, --output <list> define which output columns to use\n"), out); ++ fputs(_(" -p, --task <pid> print process namespaces\n"), out); ++ fputs(_(" -r, --raw use the raw output format\n"), out); ++ fputs(_(" -u, --notruncate don't truncate text in columns\n"), out); ++ fputs(_(" -t, --type <name> namespace type (mnt, net, ipc, user, pid, uts)\n"), out); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(USAGE_HELP, out); ++ fputs(USAGE_VERSION, out); ++ ++ fputs(_("\nAvailable columns (for --output):\n"), out); ++ ++ for (i = 0; i < ARRAY_SIZE(infos); i++) ++ fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help)); ++ ++ fprintf(out, USAGE_MAN_TAIL("lsns(8)")); ++ ++ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); ++} ++ ++ ++int main(int argc, char *argv[]) ++{ ++ struct lsns ls; ++ int c; ++ int r = 0; ++ char *outarg = NULL; ++ static const struct option long_opts[] = { ++ { "task", required_argument, NULL, 'p' }, ++ { "help", no_argument, NULL, 'h' }, ++ { "output", required_argument, NULL, 'o' }, ++ { "notruncate", no_argument, NULL, 'u' }, ++ { "version", no_argument, NULL, 'V' }, ++ { "noheadings", no_argument, NULL, 'n' }, ++ { "list", no_argument, NULL, 'l' }, ++ { "raw", no_argument, NULL, 'r' }, ++ { "type", required_argument, NULL, 't' }, ++ { NULL, 0, NULL, 0 } ++ }; ++ ++ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ ++ { 'J','r' }, ++ { 0 } ++ }; ++ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; ++ ++ setlocale(LC_ALL, ""); ++ bindtextdomain(PACKAGE, LOCALEDIR); ++ textdomain(PACKAGE); ++ atexit(close_stdout); ++ ++ lsns_init_debug(); ++ memset(&ls, 0, sizeof(ls)); ++ ++ INIT_LIST_HEAD(&ls.processes); ++ INIT_LIST_HEAD(&ls.namespaces); ++ ++ while ((c = getopt_long(argc, argv, ++ "lp:o:nruhVt:", long_opts, NULL)) != -1) { ++ ++ err_exclusive_options(c, long_opts, excl, excl_st); ++ ++ switch(c) { ++ case 'l': ++ ls.list = 1; ++ break; ++ case 'o': ++ outarg = optarg; ++ break; ++ case 'V': ++ printf(UTIL_LINUX_VERSION); ++ return EXIT_SUCCESS; ++ case 'p': ++ ls.fltr_pid = strtos32_or_err(optarg, _("invalid PID argument")); ++ break; ++ case 'h': ++ usage(stdout); ++ case 'n': ++ ls.no_headings = 1; ++ break; ++ case 'r': ++ ls.raw = 1; ++ break; ++ case 'u': ++ ls.notrunc = 1; ++ break; ++ case 't': ++ { ++ int type = ns_name2type(optarg); ++ if (type < 0) ++ errx(EXIT_FAILURE, _("unknown namespace type: %s"), optarg); ++ ls.fltr_types[type] = 1; ++ ls.fltr_ntypes++; ++ break; ++ } ++ case '?': ++ default: ++ usage(stderr); ++ } ++ } ++ ++ if (!ls.fltr_ntypes) { ++ size_t i; ++ for (i = 0; i < ARRAY_SIZE(ns_names); i++) ++ ls.fltr_types[i] = 1; ++ } ++ ++ if (optind < argc) { ++ if (ls.fltr_pid) ++ errx(EXIT_FAILURE, _("--task is mutually exclusive with <namespace>")); ++ ls.fltr_ns = strtou64_or_err(argv[optind], _("invalid namespace argument")); ++ ls.tree = ls.list ? 0 : 1; ++ ++ if (!ncolumns) { ++ columns[ncolumns++] = COL_PID; ++ columns[ncolumns++] = COL_PPID; ++ columns[ncolumns++] = COL_USER; ++ columns[ncolumns++] = COL_COMMAND; ++ } ++ } ++ ++ if (!ncolumns) { ++ columns[ncolumns++] = COL_NS; ++ columns[ncolumns++] = COL_TYPE; ++ columns[ncolumns++] = COL_NPROCS; ++ columns[ncolumns++] = COL_PID; ++ columns[ncolumns++] = COL_USER; ++ columns[ncolumns++] = COL_COMMAND; ++ } ++ ++ if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), ++ (int *) &ncolumns, column_name_to_id) < 0) ++ return EXIT_FAILURE; ++ ++ scols_init_debug(0); ++ ++ uid_cache = new_idcache(); ++ if (!uid_cache) ++ err(EXIT_FAILURE, _("failed to allocate UID cache")); ++ ++ r = read_processes(&ls); ++ if (!r) ++ r = read_namespaces(&ls); ++ if (!r) { ++ if (ls.fltr_ns) { ++ struct lsns_namespace *ns = get_namespace(&ls, ls.fltr_ns); ++ ++ if (!ns) ++ errx(EXIT_FAILURE, _("not found namespace: %ju"), (uintmax_t) ls.fltr_ns); ++ r = show_namespace_processes(&ls, ns); ++ } else ++ r = show_namespaces(&ls); ++ } ++ ++ free_idcache(uid_cache); ++ return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; ++} +-- +2.7.4 + diff --git a/SOURCES/0077-lib-strutils-make-strmode-more-generic.patch b/SOURCES/0077-lib-strutils-make-strmode-more-generic.patch new file mode 100644 index 0000000..5534e62 --- /dev/null +++ b/SOURCES/0077-lib-strutils-make-strmode-more-generic.patch @@ -0,0 +1,76 @@ +From 7bf448fe38478b6e76824fa5bbd2d2d25a48f618 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 30 Jun 2015 12:41:13 +0200 +Subject: [PATCH 77/84] lib/strutils: make strmode() more generic + +Upstream: http://github.com/karelzak/util-linux/commit/7015df4936ca320a86d2916533a17499ac5e4fcf +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1153770 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + lib/strutils.c | 36 +++++++++++++++++++----------------- + 1 file changed, 19 insertions(+), 17 deletions(-) + +diff --git a/lib/strutils.c b/lib/strutils.c +index c263b86..f9cdcbb 100644 +--- a/lib/strutils.c ++++ b/lib/strutils.c +@@ -349,37 +349,39 @@ void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmes + */ + void strmode(mode_t mode, char *str) + { ++ unsigned short i = 0; ++ + if (S_ISDIR(mode)) +- str[0] = 'd'; ++ str[i++] = 'd'; + else if (S_ISLNK(mode)) +- str[0] = 'l'; ++ str[i++] = 'l'; + else if (S_ISCHR(mode)) +- str[0] = 'c'; ++ str[i++] = 'c'; + else if (S_ISBLK(mode)) +- str[0] = 'b'; ++ str[i++] = 'b'; + else if (S_ISSOCK(mode)) +- str[0] = 's'; ++ str[i++] = 's'; + else if (S_ISFIFO(mode)) +- str[0] = 'p'; ++ str[i++] = 'p'; + else if (S_ISREG(mode)) +- str[0] = '-'; ++ str[i++] = '-'; + +- str[1] = mode & S_IRUSR ? 'r' : '-'; +- str[2] = mode & S_IWUSR ? 'w' : '-'; +- str[3] = (mode & S_ISUID ++ str[i++] = mode & S_IRUSR ? 'r' : '-'; ++ str[i++] = mode & S_IWUSR ? 'w' : '-'; ++ str[i++] = (mode & S_ISUID + ? (mode & S_IXUSR ? 's' : 'S') + : (mode & S_IXUSR ? 'x' : '-')); +- str[4] = mode & S_IRGRP ? 'r' : '-'; +- str[5] = mode & S_IWGRP ? 'w' : '-'; +- str[6] = (mode & S_ISGID ++ str[i++] = mode & S_IRGRP ? 'r' : '-'; ++ str[i++] = mode & S_IWGRP ? 'w' : '-'; ++ str[i++] = (mode & S_ISGID + ? (mode & S_IXGRP ? 's' : 'S') + : (mode & S_IXGRP ? 'x' : '-')); +- str[7] = mode & S_IROTH ? 'r' : '-'; +- str[8] = mode & S_IWOTH ? 'w' : '-'; +- str[9] = (mode & S_ISVTX ++ str[i++] = mode & S_IROTH ? 'r' : '-'; ++ str[i++] = mode & S_IWOTH ? 'w' : '-'; ++ str[i++] = (mode & S_ISVTX + ? (mode & S_IXOTH ? 't' : 'T') + : (mode & S_IXOTH ? 'x' : '-')); +- str[10] = '\0'; ++ str[i] = '\0'; + } + + /* +-- +2.7.4 + diff --git a/SOURCES/0078-lsipc-backport-new-command.patch b/SOURCES/0078-lsipc-backport-new-command.patch new file mode 100644 index 0000000..e87be77 --- /dev/null +++ b/SOURCES/0078-lsipc-backport-new-command.patch @@ -0,0 +1,2000 @@ +From 9f643efe377d2a39929f19cc09e8890afc74d9a4 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 24 Jun 2016 12:57:13 +0200 +Subject: [PATCH 78/84] lsipc: backport new command + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1153770 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + bash-completion/lsipc | 64 +++ + configure.ac | 6 + + include/xalloc.h | 7 + + sys-utils/Makemodule.am | 10 + + sys-utils/ipcs.c | 2 +- + sys-utils/ipcutils.c | 116 ++--- + sys-utils/ipcutils.h | 13 +- + sys-utils/lsipc.1 | 133 +++++ + sys-utils/lsipc.c | 1316 +++++++++++++++++++++++++++++++++++++++++++++++ + tests/functions.sh | 16 +- + tests/ts/ipcs/limits2 | 9 +- + 11 files changed, 1613 insertions(+), 79 deletions(-) + create mode 100644 bash-completion/lsipc + create mode 100644 sys-utils/lsipc.1 + create mode 100644 sys-utils/lsipc.c + +diff --git a/bash-completion/lsipc b/bash-completion/lsipc +new file mode 100644 +index 0000000..6a87393 +--- /dev/null ++++ b/bash-completion/lsipc +@@ -0,0 +1,64 @@ ++_lsipc_module() ++{ ++ local cur prev OPTS ARG ++ COMPREPLY=() ++ cur="${COMP_WORDS[COMP_CWORD]}" ++ prev="${COMP_WORDS[COMP_CWORD-1]}" ++ case $prev in ++ '-i'|'--id') ++ COMPREPLY=( $(compgen -W "id" -- $cur) ) ++ return 0 ++ ;; ++ '-h'|'--help'|'-V'|'--version') ++ return 0 ++ ;; ++ '-o'|'--output') ++ local prefix realcur OUTPUT_ALL OUTPUT ++ realcur="${cur##*,}" ++ prefix="${cur%$realcur}" ++ OUTPUT_ALL="GENERAL KEY ID OWNER PERMS CUID ++ CGID UID GID CHANGE MESSAGE USEDBYTES ++ MSGS SEND RECV LSPID LRPID SHARED BYTES ++ NATTCH STATUS ATTACH DETACH CPID LPID NSEMS ++ LASTOP" ++ for WORD in $OUTPUT_ALL; do ++ if ! [[ $prefix == *"$WORD"* ]]; then ++ OUTPUT="$WORD $OUTPUT" ++ fi ++ done ++ compopt -o nospace ++ COMPREPLY=( $(compgen -P "$prefix" -W "$OUTPUT" -S ',' -- $realcur) ) ++ return 0 ++ ;; ++ esac ++ case $cur in ++ -*) ++ OPTS="--id ++ --help ++ --version ++ --shmems ++ --queues ++ --semaphores ++ --colon-separate ++ --creator ++ --export ++ --global ++ --newline ++ --noheadings ++ --notruncate ++ --output ++ --pid ++ --print0 ++ --raw ++ --time ++ --time-format" ++ COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) ++ return 0 ++ ;; ++ esac ++ local IFS=$'\n' ++ compopt -o filenames ++ COMPREPLY=( $(compgen -f -- $cur) ) ++ return 0 ++} ++complete -F _lsipc_module lsipc +diff --git a/configure.ac b/configure.ac +index 5d9ea39..fe0a011 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1038,6 +1038,12 @@ UL_REQUIRES_BUILD([lsns], [libsmartcols]) + AM_CONDITIONAL([BUILD_LSNS], [test "x$build_lsns" = xyes]) + + ++UL_BUILD_INIT([lsipc], [check]) ++UL_REQUIRES_LINUX([lsipc]) ++UL_REQUIRES_BUILD([lsipc], [libsmartcols]) ++AM_CONDITIONAL([BUILD_LSIPC], [test "x$build_lsipc" = xyes]) ++ ++ + UL_BUILD_INIT([chcpu], [check]) + UL_REQUIRES_LINUX([chcpu]) + UL_REQUIRES_HAVE([chcpu], [cpu_set_t], [cpu_set_t type]) +diff --git a/include/xalloc.h b/include/xalloc.h +index 6342793..1a1799a 100644 +--- a/include/xalloc.h ++++ b/include/xalloc.h +@@ -19,6 +19,13 @@ + # define XALLOC_EXIT_CODE EXIT_FAILURE + #endif + ++static inline void __err_oom(const char *file, unsigned int line) ++{ ++ err(XALLOC_EXIT_CODE, "%s: %u: cannot allocate memory", file, line); ++} ++ ++#define err_oom() __err_oom(__FILE__, __LINE__) ++ + static inline __ul_alloc_size(1) + void *xmalloc(const size_t size) + { +diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am +index 9baf5a3..6badd17 100644 +--- a/sys-utils/Makemodule.am ++++ b/sys-utils/Makemodule.am +@@ -22,6 +22,16 @@ ipcs_SOURCES = sys-utils/ipcs.c \ + ipcs_LDADD = $(LDADD) libcommon.la + + ++if BUILD_LSIPC ++usrbin_exec_PROGRAMS += lsipc ++dist_man_MANS += sys-utils/lsipc.1 ++lsipc_SOURCES = sys-utils/lsipc.c \ ++ sys-utils/ipcutils.c \ ++ sys-utils/ipcutils.h ++lsipc_LDADD = $(LDADD) libcommon.la libsmartcols.la ++lsipc_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) ++endif ++ + usrbin_exec_PROGRAMS += renice + dist_man_MANS += sys-utils/renice.1 + renice_SOURCES = sys-utils/renice.c +diff --git a/sys-utils/ipcs.c b/sys-utils/ipcs.c +index 14f5f0b..1843cd5 100644 +--- a/sys-utils/ipcs.c ++++ b/sys-utils/ipcs.c +@@ -201,7 +201,7 @@ static void do_shm (char format, int unit) + _("max seg size"), lim.shmmax, "\n", 0); + ipc_print_size(unit == IPC_UNIT_DEFAULT ? IPC_UNIT_KB : unit, + _("max total shared memory"), +- lim.shmall * getpagesize(), "\n", 0); ++ (uint64_t) lim.shmall * getpagesize(), "\n", 0); + ipc_print_size(unit == IPC_UNIT_DEFAULT ? IPC_UNIT_BYTES : unit, + _("min seg size"), lim.shmmin, "\n", 0); + return; +diff --git a/sys-utils/ipcutils.c b/sys-utils/ipcutils.c +index 62d7428..51fce7b 100644 +--- a/sys-utils/ipcutils.c ++++ b/sys-utils/ipcutils.c +@@ -1,4 +1,3 @@ +- + #include <inttypes.h> + + #include "c.h" +@@ -54,8 +53,8 @@ int ipc_sem_get_limits(struct ipc_limits *lim) + + } + +- if (rc == 4) { +- struct seminfo seminfo; ++ if (rc != 4) { ++ struct seminfo seminfo = { .semmni = 0 }; + union semun arg = { .array = (ushort *) &seminfo }; + + if (semctl(0, 0, IPC_INFO, arg) < 0) +@@ -82,12 +81,15 @@ int ipc_shm_get_limits(struct ipc_limits *lim) + lim->shmmni = path_read_u64(_PATH_PROC_IPC_SHMMNI); + + } else { +- struct shminfo shminfo; ++ struct shminfo *shminfo; ++ struct shmid_ds shmbuf; + +- if (shmctl(0, IPC_INFO, (struct shmid_ds *) &shminfo) < 0) ++ if (shmctl(0, IPC_INFO, &shmbuf) < 0) + return 1; +- lim->shmmni = shminfo.shmmni; +- lim->shmall = shminfo.shmall; ++ shminfo = (struct shminfo *) &shmbuf; ++ lim->shmmni = shminfo->shmmni; ++ lim->shmall = shminfo->shmall; ++ lim->shmmax = shminfo->shmmax; + } + + return 0; +@@ -97,20 +99,24 @@ int ipc_shm_get_info(int id, struct shm_data **shmds) + { + FILE *f; + int i = 0, maxid; ++ char buf[BUFSIZ]; + struct shm_data *p; +- struct shm_info dummy; ++ struct shmid_ds dummy; + + p = *shmds = xcalloc(1, sizeof(struct shm_data)); + p->next = NULL; + + f = path_fopen("r", 0, _PATH_PROC_SYSV_SHM); + if (!f) +- goto fallback; ++ goto shm_fallback; + + while (fgetc(f) != '\n'); /* skip header */ + +- while (feof(f) == 0) { +- if (fscanf(f, ++ while (fgets(buf, sizeof(buf), f) != NULL) { ++ /* scan for the first 14-16 columns (e.g. Linux 2.6.32 has 14) */ ++ p->shm_rss = 0xdead; ++ p->shm_swp = 0xdead; ++ if (sscanf(buf, + "%d %d %o %"SCNu64 " %u %u " + "%"SCNu64 " %u %u %u %u %"SCNi64 " %"SCNi64 " %"SCNi64 + " %"SCNu64 " %"SCNu64 "\n", +@@ -129,8 +135,8 @@ int ipc_shm_get_info(int id, struct shm_data **shmds) + &p->shm_dtim, + &p->shm_ctim, + &p->shm_rss, +- &p->shm_swp) != 16) +- continue; ++ &p->shm_swp) < 14) ++ continue; /* invalid line, skipped */ + + if (id > -1) { + /* ID specified */ +@@ -153,28 +159,20 @@ int ipc_shm_get_info(int id, struct shm_data **shmds) + return i; + + /* Fallback; /proc or /sys file(s) missing. */ +-fallback: +- i = id < 0 ? 0 : id; +- +- maxid = shmctl(0, SHM_INFO, (struct shmid_ds *) &dummy); +- if (maxid < 0) +- return 0; ++shm_fallback: ++ maxid = shmctl(0, SHM_INFO, &dummy); + +- while (i <= maxid) { ++ for (int j = 0; j <= maxid; j++) { + int shmid; + struct shmid_ds shmseg; + struct ipc_perm *ipcp = &shmseg.shm_perm; + +- shmid = shmctl(i, SHM_STAT, &shmseg); +- if (shmid < 0) { +- if (-1 < id) { +- free(*shmds); +- return 0; +- } +- i++; ++ shmid = shmctl(j, SHM_STAT, &shmseg); ++ if (shmid < 0 || (id > -1 && shmid != id)) { + continue; + } + ++ i++; + p->shm_perm.key = ipcp->KEY; + p->shm_perm.id = shmid; + p->shm_perm.mode = ipcp->mode; +@@ -196,11 +194,12 @@ fallback: + p->next = xcalloc(1, sizeof(struct shm_data)); + p = p->next; + p->next = NULL; +- i++; + } else +- return 1; ++ break; + } + ++ if (i == 0) ++ free(*shmds); + return i; + } + +@@ -299,30 +298,22 @@ int ipc_sem_get_info(int id, struct sem_data **semds) + return i; + + /* Fallback; /proc or /sys file(s) missing. */ +- sem_fallback: +- i = id < 0 ? 0 : id; +- ++sem_fallback: + arg.array = (ushort *) (void *)&dummy; + maxid = semctl(0, 0, SEM_INFO, arg); +- if (maxid < 0) +- return 0; + +- while (i <= maxid) { ++ for (int j = 0; j <= maxid; j++) { + int semid; + struct semid_ds semseg; + struct ipc_perm *ipcp = &semseg.sem_perm; + arg.buf = (struct semid_ds *)&semseg; + +- semid = semctl(i, 0, SEM_STAT, arg); +- if (semid < 0) { +- if (-1 < id) { +- free(*semds); +- return 0; +- } +- i++; ++ semid = semctl(j, 0, SEM_STAT, arg); ++ if (semid < 0 || (id > -1 && semid != id)) { + continue; + } + ++ i++; + p->sem_perm.key = ipcp->KEY; + p->sem_perm.id = semid; + p->sem_perm.mode = ipcp->mode; +@@ -341,10 +332,12 @@ int ipc_sem_get_info(int id, struct sem_data **semds) + i++; + } else { + get_sem_elements(p); +- return 1; ++ break; + } + } + ++ if (i == 0) ++ free(*semds); + return i; + } + +@@ -398,10 +391,6 @@ int ipc_msg_get_info(int id, struct msg_data **msgds) + if (id > -1) { + /* ID specified */ + if (id == p->msg_perm.id) { +- /* +- * FIXME: q_qbytes are not in /proc +- * +- */ + if (msgctl(id, IPC_STAT, &msgseg) != -1) + p->q_qbytes = msgseg.msg_qbytes; + i = 1; +@@ -422,27 +411,19 @@ int ipc_msg_get_info(int id, struct msg_data **msgds) + return i; + + /* Fallback; /proc or /sys file(s) missing. */ +- msg_fallback: +- i = id < 0 ? 0 : id; +- +- maxid = msgctl(id, MSG_STAT, &dummy); +- if (maxid < 0) +- return 0; ++msg_fallback: ++ maxid = msgctl(0, MSG_INFO, &dummy); + +- while (i <= maxid) { ++ for (int j = 0; j <= maxid; j++) { + int msgid; + struct ipc_perm *ipcp = &msgseg.msg_perm; + +- msgid = msgctl(i, MSG_STAT, &msgseg); +- if (msgid < 0) { +- if (-1 < id) { +- free(*msgds); +- return 0; +- } +- i++; ++ msgid = msgctl(j, MSG_STAT, &msgseg); ++ if (msgid < 0 || (id > -1 && msgid != id)) { + continue; + } + ++ i++; + p->msg_perm.key = ipcp->KEY; + p->msg_perm.id = msgid; + p->msg_perm.mode = ipcp->mode; +@@ -463,11 +444,12 @@ int ipc_msg_get_info(int id, struct msg_data **msgds) + p->next = xcalloc(1, sizeof(struct msg_data)); + p = p->next; + p->next = NULL; +- i++; + } else +- return 1; ++ break; + } + ++ if (i == 0) ++ free(*msgds); + return i; + } + +@@ -508,10 +490,10 @@ void ipc_print_perms(FILE *f, struct ipc_stat *is) + fprintf(f, " %-10u\n", is->gid); + } + +-void ipc_print_size(int unit, char *msg, size_t size, const char *end, ++void ipc_print_size(int unit, char *msg, uint64_t size, const char *end, + int width) + { +- char format[16]; ++ char format[32]; + + if (!msg) + /* NULL */ ; +@@ -527,11 +509,11 @@ void ipc_print_size(int unit, char *msg, size_t size, const char *end, + switch (unit) { + case IPC_UNIT_DEFAULT: + case IPC_UNIT_BYTES: +- sprintf(format, "%%%dzu", width); ++ sprintf(format, "%%%dju", width); + printf(format, size); + break; + case IPC_UNIT_KB: +- sprintf(format, "%%%dzu", width); ++ sprintf(format, "%%%dju", width); + printf(format, size / 1024); + break; + case IPC_UNIT_HUMAN: +diff --git a/sys-utils/ipcutils.h b/sys-utils/ipcutils.h +index d2e5972..444065a 100644 +--- a/sys-utils/ipcutils.h ++++ b/sys-utils/ipcutils.h +@@ -12,6 +12,7 @@ + #include <unistd.h> + #include <grp.h> + #include <pwd.h> ++#include <stdint.h> + + /* + * SHM_DEST and SHM_LOCKED are defined in kernel headers, but inside +@@ -34,11 +35,11 @@ + # define SHM_INFO 14 + struct shm_info { + int used_ids; +- ulong shm_tot; /* total allocated shm */ +- ulong shm_rss; /* total resident shm */ +- ulong shm_swp; /* total swapped shm */ +- ulong swap_attempts; +- ulong swap_successes; ++ unsigned long shm_tot; /* total allocated shm */ ++ unsigned long shm_rss; /* total resident shm */ ++ unsigned long shm_swp; /* total swapped shm */ ++ unsigned long swap_attempts; ++ unsigned long swap_successes; + }; + #endif + +@@ -118,7 +119,7 @@ struct ipc_stat { + }; + + extern void ipc_print_perms(FILE *f, struct ipc_stat *is); +-extern void ipc_print_size(int unit, char *msg, size_t size, const char *end, int width); ++extern void ipc_print_size(int unit, char *msg, uint64_t size, const char *end, int width); + + /* See 'struct shmid_kernel' in kernel sources + */ +diff --git a/sys-utils/lsipc.1 b/sys-utils/lsipc.1 +new file mode 100644 +index 0000000..98449cb +--- /dev/null ++++ b/sys-utils/lsipc.1 +@@ -0,0 +1,133 @@ ++.\" Copyright 2015 Ondrej Oprala(ooprala@redhat.com) ++.\" May be distributed under the GNU General Public License ++.TH LSIPC "1" "November 2015" "util-linux" "User Commands" ++.SH NAME ++lsipc \- show information on IPC facilities currently employed in the system ++.SH SYNOPSIS ++.B lsipc ++[options] ++.SH DESCRIPTION ++.B lsipc ++shows information on the inter-process communication facilities ++for which the calling process has read access. ++.SH OPTIONS ++.TP ++\fB\-i\fR, \fB\-\-id\fR \fIid\fR ++Show full details on just the one resource element identified by ++.IR id . ++This option needs to be combined with one of the three resource options: ++.BR \-m , ++.BR \-q " or" ++.BR \-s . ++It is possible to override the default output format for this option with the ++\fB\-\-list\fR, \fB\-\-raw\fR, \fB\-\-json\fR or \fB\-\-export\fR option. ++.TP ++\fB\-g\fR, \fB\-\-global\fR ++Show system-wide usage and limits of IPC resources. ++This option may be combined with one of the three resource options: ++.BR \-m , ++.BR \-q " or" ++.BR \-s . ++The default is to show information about all resources. ++.TP ++\fB\-h\fR, \fB\-\-help\fR ++Display help text and exit. ++.TP ++\fB\-V\fR, \fB\-\-version\fR ++Display version information and exit. ++.SS "Resource options" ++.TP ++\fB\-m\fR, \fB\-\-shmems\fR ++Write information about active shared memory segments. ++.TP ++\fB\-q\fR, \fB\-\-queues\fR ++Write information about active message queues. ++.TP ++\fB\-s\fR, \fB\-\-semaphores\fR ++Write information about active semaphore sets. ++.SS "Output formatting" ++.TP ++\fB\-c\fR, \fB\-\-creator\fR ++Show creator and owner. ++.TP ++\fB\-e\fR, \fB\-\-export\fR ++Output data in the format of NAME=VALUE. ++.TP ++\fB\-l\fR, \fB\-\-list\fR ++Use the list output format. This is the default, except when \fB\-\-id\fR ++is used. ++.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\-\-pid\fR ++Show PIDs of creator and last operator. ++.TP ++\fB\-r\fR, \fB\-\-raw\fR ++Raw output (no columnation). ++.TP ++\fB\-t\fR, \fB\-\-time\fR ++Write time information. The time of the last control operation that changed ++the access permissions for all facilities, the time of the last ++.I msgsnd() ++and ++.I msgrcv() ++operations on message queues, the time of the last ++.I shmat() ++and ++.I shmdt() ++operations on shared memory, and the time of the last ++.I semop() ++operation on semaphores. ++.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. ++ ++.SH EXIT STATUS ++.TP ++0 ++if OK, ++.TP ++1 ++if incorrect arguments specified, ++.TP ++2 ++if a serious error occurs. ++.SH SEE ALSO ++.BR ipcrm (1), ++.BR ipcmk (1), ++.BR msgrcv (2), ++.BR msgsnd (2), ++.BR semget (2), ++.BR semop (2), ++.BR shmat (2), ++.BR shmdt (2), ++.BR shmget (2) ++.SH HISTORY ++The \fBlsipc\fP utility is inspired by the \fBipcs\fP utility. ++.SH AUTHORS ++.MT ooprala@redhat.com ++Ondrej Oprala ++.ME ++.br ++.MT kzak@redhat.com ++Karel Zak ++.ME ++ ++.SH AVAILABILITY ++The lsipc 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 --git a/sys-utils/lsipc.c b/sys-utils/lsipc.c +new file mode 100644 +index 0000000..0be9d91 +--- /dev/null ++++ b/sys-utils/lsipc.c +@@ -0,0 +1,1316 @@ ++/* ++ * lsipc - List information about IPC instances employed in the system ++ * ++ * Copyright (C) 2015 Ondrej Oprala <ooprala@redhat.com> ++ * Copyright (C) 2015 Karel Zak <ooprala@redhat.com> ++ * ++ * 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. ++ * ++ * ++ * lsipc is inspired by the ipcs utility. The aim is to create ++ * a utility unencumbered by a standard to provide more flexible ++ * means of controlling the output. ++ */ ++ ++#include <errno.h> ++#include <getopt.h> ++#include <sys/time.h> ++#include <unistd.h> ++ ++#include <libsmartcols.h> ++ ++#include "c.h" ++#include "nls.h" ++#include "closestream.h" ++#include "strutils.h" ++#include "optutils.h" ++#include "xalloc.h" ++#include "procutils.h" ++#include "ipcutils.h" ++ ++/* ++ * time modes ++ * */ ++enum { ++ TIME_INVALID = 0, ++ TIME_SHORT, ++ TIME_FULL, ++ TIME_ISO ++}; ++ ++/* ++ * IDs ++ */ ++enum { ++ /* generic */ ++ COLDESC_IDX_GEN_FIRST = 0, ++ COL_KEY = COLDESC_IDX_GEN_FIRST, ++ COL_ID, ++ COL_OWNER, ++ COL_PERMS, ++ COL_CUID, ++ COL_CUSER, ++ COL_CGID, ++ COL_CGROUP, ++ COL_UID, ++ COL_USER, ++ COL_GID, ++ COL_GROUP, ++ COL_CTIME, ++ COLDESC_IDX_GEN_LAST = COL_CTIME, ++ ++ /* msgq-specific */ ++ COLDESC_IDX_MSG_FIRST, ++ COL_USEDBYTES = COLDESC_IDX_MSG_FIRST, ++ COL_MSGS, ++ COL_SEND, ++ COL_RECV, ++ COL_LSPID, ++ COL_LRPID, ++ COLDESC_IDX_MSG_LAST = COL_LRPID, ++ ++ /* shm-specific */ ++ COLDESC_IDX_SHM_FIRST, ++ COL_SIZE = COLDESC_IDX_SHM_FIRST, ++ COL_NATTCH, ++ COL_STATUS, ++ COL_ATTACH, ++ COL_DETACH, ++ COL_COMMAND, ++ COL_CPID, ++ COL_LPID, ++ COLDESC_IDX_SHM_LAST = COL_LPID, ++ ++ /* sem-specific */ ++ COLDESC_IDX_SEM_FIRST, ++ COL_NSEMS = COLDESC_IDX_SEM_FIRST, ++ COL_OTIME, ++ COLDESC_IDX_SEM_LAST = COL_OTIME, ++ ++ /* summary (--global) */ ++ COLDESC_IDX_SUM_FIRST, ++ COL_RESOURCE = COLDESC_IDX_SUM_FIRST, ++ COL_DESC, ++ COL_LIMIT, ++ COL_USED, ++ COL_USEPERC, ++ COLDESC_IDX_SUM_LAST = COL_USEPERC ++}; ++ ++/* not all columns apply to all options, so we specify a legal range for each */ ++static size_t LOWER, UPPER; ++ ++/* ++ * output modes ++ */ ++enum { ++ OUT_EXPORT = 1, ++ OUT_NEWLINE, ++ OUT_RAW, ++ OUT_PRETTY, ++ OUT_LIST ++}; ++ ++struct lsipc_control { ++ int outmode; ++ unsigned int noheadings : 1, /* don't print header line */ ++ notrunc : 1, /* don't truncate columns */ ++ bytes : 1, /* SIZE in bytes */ ++ numperms : 1, /* numeric permissions */ ++ time_mode : 2; ++}; ++ ++struct lsipc_coldesc { ++ const char *name; ++ const char *help; ++ const char *pretty_name; ++ ++ double whint; /* width hint */ ++ long flag; ++}; ++ ++static const struct lsipc_coldesc coldescs[] = ++{ ++ /* common */ ++ [COL_KEY] = { "KEY", N_("Resource key"), N_("Key"), 1}, ++ [COL_ID] = { "ID", N_("Resource ID"), N_("ID"), 1}, ++ [COL_OWNER] = { "OWNER", N_("Owner's username or UID"), N_("Owner"), 1, SCOLS_FL_RIGHT}, ++ [COL_PERMS] = { "PERMS", N_("Permissions"), N_("Permissions"), 1, SCOLS_FL_RIGHT}, ++ [COL_CUID] = { "CUID", N_("Creator UID"), N_("Creator UID"), 1, SCOLS_FL_RIGHT}, ++ [COL_CUSER] = { "CUSER", N_("Creator user"), N_("Creator user"), 1 }, ++ [COL_CGID] = { "CGID", N_("Creator GID"), N_("Creator GID"), 1, SCOLS_FL_RIGHT}, ++ [COL_CGROUP] = { "CGROUP", N_("Creator group"), N_("Creator group"), 1 }, ++ [COL_UID] = { "UID", N_("User ID"), N_("UID"), 1, SCOLS_FL_RIGHT}, ++ [COL_USER] = { "USER", N_("User name"), N_("User name"), 1}, ++ [COL_GID] = { "GID", N_("Group ID"), N_("GID"), 1, SCOLS_FL_RIGHT}, ++ [COL_GROUP] = { "GROUP", N_("Group name"), N_("Group name"), 1}, ++ [COL_CTIME] = { "CTIME", N_("Time of the last change"), N_("Last change"), 1, SCOLS_FL_RIGHT}, ++ ++ /* msgq-specific */ ++ [COL_USEDBYTES] = { "USEDBYTES",N_("Bytes used"), N_("Bytes used"), 1, SCOLS_FL_RIGHT}, ++ [COL_MSGS] = { "MSGS", N_("Number of messages"), N_("Messages"), 1}, ++ [COL_SEND] = { "SEND", N_("Time of last msg sent"), N_("Msg sent"), 1, SCOLS_FL_RIGHT}, ++ [COL_RECV] = { "RECV", N_("Time of last msg received"), N_("Msg received"), 1, SCOLS_FL_RIGHT}, ++ [COL_LSPID] = { "LSPID", N_("PID of the last msg sender"), N_("Msg sender"), 1, SCOLS_FL_RIGHT}, ++ [COL_LRPID] = { "LRPID", N_("PID of the last msg receiver"), N_("Msg receiver"), 1, SCOLS_FL_RIGHT}, ++ ++ /* shm-specific */ ++ [COL_SIZE] = { "SIZE", N_("Segment size"), N_("Segment size"), 1, SCOLS_FL_RIGHT}, ++ [COL_NATTCH] = { "NATTCH", N_("Number of attached processes"), N_("Attached processes"), 1, SCOLS_FL_RIGHT}, ++ [COL_STATUS] = { "STATUS", N_("Status"), N_("Status"), 1, SCOLS_FL_NOEXTREMES}, ++ [COL_ATTACH] = { "ATTACH", N_("Attach time"), N_("Attach time"), 1, SCOLS_FL_RIGHT}, ++ [COL_DETACH] = { "DETACH", N_("Detach time"), N_("Detach time"), 1, SCOLS_FL_RIGHT}, ++ [COL_COMMAND] = { "COMMAND", N_("Creator command line"), N_("Creator command"), 0, SCOLS_FL_TRUNC}, ++ [COL_CPID] = { "CPID", N_("PID of the creator"), N_("Creator PID"), 1, SCOLS_FL_RIGHT}, ++ [COL_LPID] = { "LPID", N_("PID of last user"), N_("Last user PID"), 1, SCOLS_FL_RIGHT}, ++ ++ /* sem-specific */ ++ [COL_NSEMS] = { "NSEMS", N_("Number of semaphores"), N_("Semaphores"), 1, SCOLS_FL_RIGHT}, ++ [COL_OTIME] = { "OTIME", N_("Time of the last operation"), N_("Last operation"), 1, SCOLS_FL_RIGHT}, ++ ++ /* cols for summarized information */ ++ [COL_RESOURCE] = { "RESOURCE", N_("Resource name"), N_("Resource"), 1 }, ++ [COL_DESC] = { "DESCRIPTION",N_("Resource description"), N_("Description"), 1 }, ++ [COL_USED] = { "USED", N_("Currently used"), N_("Used"), 1, SCOLS_FL_RIGHT }, ++ [COL_USEPERC] = { "USE%", N_("Currently use percentage"), N_("Use"), 1, SCOLS_FL_RIGHT }, ++ [COL_LIMIT] = { "LIMIT", N_("System-wide limit"), N_("Limit"), 1, SCOLS_FL_RIGHT }, ++}; ++ ++ ++/* 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 size_t 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 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)) { ++ if (i > COL_CTIME) { ++ if (i >= LOWER && i <= UPPER) ++ return i; ++ else { ++ warnx(_("column %s does not apply to the specified IPC"), name); ++ return -1; ++ } ++ } else ++ return i; ++ } ++ } ++ warnx(_("unknown column: %s"), name); ++ return -1; ++} ++ ++static char *get_username(struct passwd **pw, uid_t id) ++{ ++ if (!*pw || (*pw)->pw_uid != id) ++ *pw = getpwuid(id); ++ ++ return *pw ? xstrdup((*pw)->pw_name) : NULL; ++} ++ ++static char *get_groupname(struct group **gr, gid_t id) ++{ ++ if (!*gr || (*gr)->gr_gid != id) ++ *gr = getgrgid(id); ++ ++ return *gr ? xstrdup((*gr)->gr_name) : NULL; ++} ++ ++static int parse_time_mode(const char *optarg) ++{ ++ struct lsipc_timefmt { ++ const char *name; ++ const int val; ++ }; ++ static const struct lsipc_timefmt timefmts[] = { ++ {"iso", TIME_ISO}, ++ {"full", TIME_FULL}, ++ {"short", TIME_SHORT}, ++ }; ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(timefmts); i++) { ++ if (strcmp(timefmts[i].name, optarg) == 0) ++ return timefmts[i].val; ++ } ++ errx(EXIT_FAILURE, _("unknown time format: %s"), optarg); ++} ++ ++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_SEPARATOR, out); ++ fputs(_("Show information on IPC facilities.\n"), out); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Resource options:\n"), out); ++ fputs(_(" -m, --shmems shared memory segments\n"), out); ++ fputs(_(" -q, --queues message queues\n"), out); ++ fputs(_(" -s, --semaphores semaphores\n"), out); ++ fputs(_(" -g, --global info about system-wide usage (may be used with -m, -q and -s)\n"), out); ++ fputs(_(" -i, --id <id> print details on resource identified by <id>\n"), out); ++ ++ fputs(USAGE_OPTIONS, out); ++ fputs(_(" --noheadings don't print headings\n"), out); ++ fputs(_(" --notruncate don't truncate output\n"), out); ++ fputs(_(" --time-format=<type> display dates in short, full or iso format\n"), out); ++ fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out); ++ fputs(_(" -c, --creator show creator and owner\n"), out); ++ fputs(_(" -e, --export display in an export-able output format\n"), out); ++ fputs(_(" -n, --newline display each piece of information on a new line\n"), out); ++ fputs(_(" -l, --list force list output format (for example with --id)\n"), out); ++ fputs(_(" -o, --output[=<list>] define the columns to output\n"), out); ++ fputs(_(" -P, --numeric-perms print numeric permissions (PERMS column)\n"), out); ++ fputs(_(" -r, --raw display in raw mode\n"), out); ++ fputs(_(" -t, --time show attach, detach and change times\n"), out); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(USAGE_HELP, out); ++ fputs(USAGE_VERSION, out); ++ ++ fprintf(out, _("\nGeneric columns:\n")); ++ for (i = COLDESC_IDX_GEN_FIRST; i <= COLDESC_IDX_GEN_LAST; i++) ++ fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); ++ ++ fprintf(out, _("\nShared-memory columns (--shmems):\n")); ++ for (i = COLDESC_IDX_SHM_FIRST; i <= COLDESC_IDX_SHM_LAST; i++) ++ fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); ++ ++ fprintf(out, _("\nMessage-queue columns (--queues):\n")); ++ for (i = COLDESC_IDX_MSG_FIRST; i <= COLDESC_IDX_MSG_LAST; i++) ++ fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); ++ ++ fprintf(out, _("\nSemaphore columns (--semaphores):\n")); ++ for (i = COLDESC_IDX_SEM_FIRST; i <= COLDESC_IDX_SEM_LAST; i++) ++ fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); ++ ++ fprintf(out, _("\nSummary columns (--global):\n")); ++ for (i = COLDESC_IDX_SUM_FIRST; i <= COLDESC_IDX_SUM_LAST; i++) ++ fprintf(out, " %14s %s\n", coldescs[i].name, _(coldescs[i].help)); ++ ++ fprintf(out, USAGE_MAN_TAIL("lsipc(1)")); ++ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); ++} ++ ++static struct libscols_table *new_table(struct lsipc_control *ctl) ++{ ++ struct libscols_table *table = scols_new_table(); ++ ++ if (!table) ++ errx(EXIT_FAILURE, _("failed to initialize output table")); ++ if (ctl->noheadings) ++ scols_table_enable_noheadings(table, 1); ++ ++ switch(ctl->outmode) { ++ case OUT_NEWLINE: ++ scols_table_set_column_separator(table, "\n"); ++ /* fallthrough */ ++ case OUT_EXPORT: ++ scols_table_enable_export(table, 1); ++ break; ++ case OUT_RAW: ++ scols_table_enable_raw(table, 1); ++ break; ++ case OUT_PRETTY: ++ scols_table_enable_noheadings(table, 1); ++ break; ++ default: ++ break; ++ } ++ return table; ++} ++ ++static struct libscols_table *setup_table(struct lsipc_control *ctl) ++{ ++ struct libscols_table *table = new_table(ctl); ++ size_t n; ++ ++ for (n = 0; n < ncolumns; n++) { ++ int flags = coldescs[columns[n]].flag; ++ ++ if (ctl->notrunc) ++ flags &= ~SCOLS_FL_TRUNC; ++ ++ if (!scols_table_new_column(table, ++ coldescs[columns[n]].name, ++ coldescs[columns[n]].whint, ++ flags)) ++ goto fail; ++ } ++ return table; ++fail: ++ scols_unref_table(table); ++ return NULL; ++} ++ ++static int print_pretty(struct libscols_table *table) ++{ ++ 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(table, 0); ++ while (!scols_table_next_column(table, itr, &col)) { ++ ++ data = scols_line_get_cell(ln, n); ++ ++ hstr = N_(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; ++ } ++ ++ /* this is used to pretty-print detailed info about a semaphore array */ ++ if (ln) { ++ struct libscols_table *subtab = scols_line_get_userdata(ln); ++ if (subtab) { ++ printf(_("Elements:\n\n")); ++ scols_print_table(subtab); ++ } ++ } ++ ++ scols_free_iter(itr); ++ return 0; ++ ++} ++ ++static int print_table(struct lsipc_control *ctl, struct libscols_table *tb) ++{ ++ if (ctl->outmode == OUT_PRETTY) ++ print_pretty(tb); ++ else ++ scols_print_table(tb); ++ return 0; ++} ++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 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", &tm); ++ else if (date_is_thisyear(time)) ++ strftime(buf, sizeof(buf), "%b%d", &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 void global_set_data(struct libscols_table *tb, const char *resource, ++ const char *desc, uintmax_t used, uintmax_t limit, int usage) ++{ ++ struct libscols_line *ln; ++ size_t n; ++ ++ ln = scols_table_new_line(tb, NULL); ++ if (!ln) ++ err_oom(); ++ ++ for (n = 0; n < ncolumns; n++) { ++ int rc = 0; ++ char *arg = NULL; ++ ++ switch (columns[n]) { ++ case COL_RESOURCE: ++ rc = scols_line_set_data(ln, n, resource); ++ break; ++ case COL_DESC: ++ rc = scols_line_set_data(ln, n, desc); ++ break; ++ case COL_USED: ++ if (usage) { ++ xasprintf(&arg, "%ju", used); ++ rc = scols_line_refer_data(ln, n, arg); ++ } else ++ rc = scols_line_set_data(ln, n, "-"); ++ break; ++ case COL_USEPERC: ++ if (usage) { ++ xasprintf(&arg, "%2.2f%%", (double) used / limit * 100); ++ rc = scols_line_refer_data(ln, n, arg); ++ } else ++ rc = scols_line_set_data(ln, n, "-"); ++ break; ++ case COL_LIMIT: ++ xasprintf(&arg, "%ju", limit); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ } ++ ++ if (rc != 0) ++ err(EXIT_FAILURE, _("failed to set data")); ++ } ++} ++ ++static void setup_sem_elements_columns(struct libscols_table *tb) ++{ ++ if (!scols_table_new_column(tb, "SEMNUM", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++ if (!scols_table_new_column(tb, "VALUE", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++ if (!scols_table_new_column(tb, "NCOUNT", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++ if (!scols_table_new_column(tb, "ZCOUNT", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++ if (!scols_table_new_column(tb, "PID", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++ if (!scols_table_new_column(tb, "COMMAND", 0, SCOLS_FL_RIGHT)) ++ err_oom(); ++} ++ ++static void do_sem(int id, struct lsipc_control *ctl, struct libscols_table *tb) ++{ ++ struct libscols_line *ln; ++ struct passwd *pw = NULL, *cpw = NULL; ++ struct group *gr = NULL, *cgr = NULL; ++ struct sem_data *semds, *semdsp; ++ char *arg = NULL; ++ ++ if (ipc_sem_get_info(id, &semds) < 1) { ++ if (id > -1) ++ warnx(_("id %d not found"), id); ++ return; ++ } ++ for (semdsp = semds; semdsp->next != NULL || id > -1; semdsp = semdsp->next) { ++ size_t n; ++ ln = scols_table_new_line(tb, NULL); ++ ++ for (n = 0; n < ncolumns; n++) { ++ int rc = 0; ++ switch (columns[n]) { ++ case COL_KEY: ++ xasprintf(&arg, "0x%08x",semdsp->sem_perm.key); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_ID: ++ xasprintf(&arg, "%d",semdsp->sem_perm.id); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_OWNER: ++ arg = get_username(&pw, semdsp->sem_perm.uid); ++ if (!arg) ++ xasprintf(&arg, "%u", semdsp->sem_perm.uid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_PERMS: ++ if (ctl->numperms) ++ xasprintf(&arg, "%#o", semdsp->sem_perm.mode & 0777); ++ else { ++ arg = xmalloc(11); ++ strmode(semdsp->sem_perm.mode & 0777, arg); ++ } ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CUID: ++ xasprintf(&arg, "%u", semdsp->sem_perm.cuid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CUSER: ++ arg = get_username(&cpw, semdsp->sem_perm.cuid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGID: ++ xasprintf(&arg, "%u", semdsp->sem_perm.cgid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGROUP: ++ arg = get_groupname(&cgr, semdsp->sem_perm.cgid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_UID: ++ xasprintf(&arg, "%u", semdsp->sem_perm.uid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_USER: ++ arg = get_username(&pw, semdsp->sem_perm.uid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GID: ++ xasprintf(&arg, "%u", semdsp->sem_perm.gid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GROUP: ++ arg = get_groupname(&gr, semdsp->sem_perm.gid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CTIME: ++ if (semdsp->sem_ctime != 0) { ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)semdsp->sem_ctime)); ++ } ++ break; ++ case COL_NSEMS: ++ xasprintf(&arg, "%ju", semdsp->sem_nsems); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_OTIME: ++ if (semdsp->sem_otime != 0) { ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)semdsp->sem_otime)); ++ } ++ break; ++ } ++ if (rc != 0) ++ err(EXIT_FAILURE, _("failed to set data")); ++ arg = NULL; ++ } ++ ++ if (id > -1 && semds->sem_nsems) { ++ /* Create extra table with ID specific semaphore elements */ ++ struct libscols_table *sub = new_table(ctl); ++ size_t i; ++ int rc = 0; ++ ++ scols_table_enable_noheadings(sub, 0); ++ setup_sem_elements_columns(sub); ++ ++ for (i = 0; i < semds->sem_nsems; i++) { ++ struct sem_elem *e = &semds->elements[i]; ++ struct libscols_line *sln = scols_table_new_line(sub, NULL); ++ ++ /* SEMNUM */ ++ xasprintf(&arg, "%zu", i); ++ rc = scols_line_refer_data(sln, 0, arg); ++ if (rc) ++ break; ++ ++ /* VALUE */ ++ xasprintf(&arg, "%d", e->semval); ++ rc = scols_line_refer_data(sln, 1, arg); ++ if (rc) ++ break; ++ ++ /* NCOUNT */ ++ xasprintf(&arg, "%d", e->ncount); ++ rc = scols_line_refer_data(sln, 2, arg); ++ if (rc) ++ break; ++ ++ /* ZCOUNT */ ++ xasprintf(&arg, "%d", e->zcount); ++ rc = scols_line_refer_data(sln, 3, arg); ++ if (rc) ++ break; ++ ++ /* PID */ ++ xasprintf(&arg, "%d", e->pid); ++ rc = scols_line_refer_data(sln, 4, arg); ++ if (rc) ++ break; ++ ++ /* COMMAND */ ++ arg = proc_get_command(e->pid); ++ rc = scols_line_refer_data(sln, 5, arg); ++ if (rc) ++ break; ++ } ++ ++ if (rc != 0) ++ err(EXIT_FAILURE, _("failed to set data")); ++ ++ scols_line_set_userdata(ln, (void *)sub); ++ break; ++ } ++ } ++ ipc_sem_free_info(semds); ++} ++ ++static void do_sem_global(struct libscols_table *tb) ++{ ++ struct sem_data *semds, *semdsp; ++ struct ipc_limits lim; ++ int nsems = 0, nsets = 0; ++ ++ ipc_sem_get_limits(&lim); ++ ++ if (ipc_sem_get_info(-1, &semds) > 0) { ++ for (semdsp = semds; semdsp->next != NULL; semdsp = semdsp->next) { ++ ++nsets; ++ nsems += semds->sem_nsems; ++ } ++ ipc_sem_free_info(semds); ++ } ++ ++ global_set_data(tb, "SEMMNI", _("Number of semaphore identifiers"), nsets, lim.semmni, 1); ++ global_set_data(tb, "SEMMNS", _("Total number of semaphores"), nsems, lim.semmns, 1); ++ global_set_data(tb, "SEMMSL", _("Max semaphores per semaphore set."), 0, lim.semmsl, 0); ++ global_set_data(tb, "SEMOPM", _("Max number of operations per semop(2)"), 0, lim.semopm, 0); ++ global_set_data(tb, "SEMVMX", _("Semaphore max value"), 0, lim.semvmx, 0); ++} ++ ++static void do_msg(int id, struct lsipc_control *ctl, struct libscols_table *tb) ++{ ++ struct libscols_line *ln; ++ struct passwd *pw = NULL; ++ struct group *gr = NULL; ++ struct msg_data *msgds, *msgdsp; ++ char *arg = NULL; ++ ++ if (ipc_msg_get_info(id, &msgds) < 1) { ++ if (id > -1) ++ warnx(_("id %d not found"), id); ++ return; ++ } ++ ++ for (msgdsp = msgds; msgdsp->next != NULL || id > -1 ; msgdsp = msgdsp->next) { ++ size_t n; ++ ln = scols_table_new_line(tb, NULL); ++ ++ /* no need to call getpwuid() for the same user */ ++ if (!(pw && pw->pw_uid == msgdsp->msg_perm.uid)) ++ pw = getpwuid(msgdsp->msg_perm.uid); ++ ++ /* no need to call getgrgid() for the same user */ ++ if (!(gr && gr->gr_gid == msgdsp->msg_perm.gid)) ++ gr = getgrgid(msgdsp->msg_perm.gid); ++ ++ for (n = 0; n < ncolumns; n++) { ++ int rc = 0; ++ ++ switch (columns[n]) { ++ case COL_KEY: ++ xasprintf(&arg, "0x%08x",msgdsp->msg_perm.key); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_ID: ++ xasprintf(&arg, "%d",msgdsp->msg_perm.id); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_OWNER: ++ arg = get_username(&pw, msgdsp->msg_perm.uid); ++ if (!arg) ++ xasprintf(&arg, "%u", msgdsp->msg_perm.uid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_PERMS: ++ if (ctl->numperms) ++ xasprintf(&arg, "%#o", msgdsp->msg_perm.mode & 0777); ++ else { ++ arg = xmalloc(11); ++ strmode(msgdsp->msg_perm.mode & 0777, arg); ++ rc = scols_line_refer_data(ln, n, arg); ++ } ++ break; ++ case COL_CUID: ++ xasprintf(&arg, "%u", msgdsp->msg_perm.cuid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CUSER: ++ arg = get_username(&pw, msgdsp->msg_perm.cuid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGID: ++ xasprintf(&arg, "%u", msgdsp->msg_perm.cuid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGROUP: ++ arg = get_groupname(&gr, msgdsp->msg_perm.cgid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_UID: ++ xasprintf(&arg, "%u", msgdsp->msg_perm.uid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_USER: ++ arg = get_username(&pw, msgdsp->msg_perm.uid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GID: ++ xasprintf(&arg, "%u", msgdsp->msg_perm.gid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GROUP: ++ arg = get_groupname(&gr,msgdsp->msg_perm.gid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CTIME: ++ if (msgdsp->q_ctime != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)msgdsp->q_ctime)); ++ break; ++ case COL_USEDBYTES: ++ xasprintf(&arg, "%ju", msgdsp->q_cbytes); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_MSGS: ++ xasprintf(&arg, "%ju", msgdsp->q_qnum); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_SEND: ++ if (msgdsp->q_stime != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)msgdsp->q_stime)); ++ break; ++ case COL_RECV: ++ if (msgdsp->q_rtime != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)msgdsp->q_rtime)); ++ break; ++ case COL_LSPID: ++ xasprintf(&arg, "%u", msgdsp->q_lspid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_LRPID: ++ xasprintf(&arg, "%u", msgdsp->q_lrpid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ } ++ if (rc != 0) ++ err(EXIT_FAILURE, _("failed to set data")); ++ arg = NULL; ++ } ++ if (id > -1) ++ break; ++ } ++ ipc_msg_free_info(msgds); ++} ++ ++ ++static void do_msg_global(struct libscols_table *tb) ++{ ++ struct msg_data *msgds, *msgdsp; ++ struct ipc_limits lim; ++ int msgqs = 0; ++ ++ ipc_msg_get_limits(&lim); ++ ++ /* count number of used queues */ ++ if (ipc_msg_get_info(-1, &msgds) > 0) { ++ for (msgdsp = msgds; msgdsp->next != NULL; msgdsp = msgdsp->next) ++ ++msgqs; ++ ipc_msg_free_info(msgds); ++ } ++ ++ global_set_data(tb, "MSGMNI", _("Number of message queues"), msgqs, lim.msgmni, 1); ++ global_set_data(tb, "MSGMAX", _("Max size of message (bytes)"), 0, lim.msgmax, 0); ++ global_set_data(tb, "MSGMNB", _("Default max size of queue (bytes)"), 0, lim.msgmnb, 0); ++} ++ ++ ++static void do_shm(int id, struct lsipc_control *ctl, struct libscols_table *tb) ++{ ++ struct libscols_line *ln; ++ struct passwd *pw = NULL; ++ struct group *gr = NULL; ++ struct shm_data *shmds, *shmdsp; ++ char *arg = NULL; ++ ++ if (ipc_shm_get_info(id, &shmds) < 1) { ++ if (id > -1) ++ warnx(_("id %d not found"), id); ++ return; ++ } ++ ++ for (shmdsp = shmds; shmdsp->next != NULL || id > -1 ; shmdsp = shmdsp->next) { ++ size_t n; ++ ln = scols_table_new_line(tb, NULL); ++ if (!ln) ++ err_oom(); ++ ++ for (n = 0; n < ncolumns; n++) { ++ int rc = 0; ++ ++ switch (columns[n]) { ++ case COL_KEY: ++ xasprintf(&arg, "0x%08x",shmdsp->shm_perm.key); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_ID: ++ xasprintf(&arg, "%d",shmdsp->shm_perm.id); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_OWNER: ++ arg = get_username(&pw, shmdsp->shm_perm.uid); ++ if (!arg) ++ xasprintf(&arg, "%u", shmdsp->shm_perm.uid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_PERMS: ++ if (ctl->numperms) ++ xasprintf(&arg, "%#o", shmdsp->shm_perm.mode & 0777); ++ else { ++ arg = xmalloc(11); ++ strmode(shmdsp->shm_perm.mode & 0777, arg); ++ } ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CUID: ++ xasprintf(&arg, "%u", shmdsp->shm_perm.cuid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CUSER: ++ arg = get_username(&pw, shmdsp->shm_perm.cuid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGID: ++ xasprintf(&arg, "%u", shmdsp->shm_perm.cuid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CGROUP: ++ arg = get_groupname(&gr, shmdsp->shm_perm.cgid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_UID: ++ xasprintf(&arg, "%u", shmdsp->shm_perm.uid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_USER: ++ arg = get_username(&pw, shmdsp->shm_perm.uid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GID: ++ xasprintf(&arg, "%u", shmdsp->shm_perm.gid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_GROUP: ++ arg = get_groupname(&gr, shmdsp->shm_perm.gid); ++ if (arg) ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_CTIME: ++ if (shmdsp->shm_ctim != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)shmdsp->shm_ctim)); ++ break; ++ case COL_SIZE: ++ if (ctl->bytes) ++ xasprintf(&arg, "%ju", shmdsp->shm_segsz); ++ else ++ arg = size_to_human_string(SIZE_SUFFIX_1LETTER, shmdsp->shm_segsz); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_NATTCH: ++ xasprintf(&arg, "%ju", shmdsp->shm_nattch); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_STATUS: { ++ int comma = 0; ++ size_t offt = 0; ++ ++ free(arg); ++ arg = xcalloc(1, sizeof(char) * strlen(_("dest")) ++ + strlen(_("locked")) ++ + strlen(_("hugetlb")) ++ + strlen(_("noreserve")) + 4); ++#ifdef SHM_DEST ++ if (shmdsp->shm_perm.mode & SHM_DEST) { ++ offt += sprintf(arg, "%s", _("dest")); ++ comma++; ++ } ++#endif ++#ifdef SHM_LOCKED ++ if (shmdsp->shm_perm.mode & SHM_LOCKED) { ++ if (comma) ++ arg[offt++] = ','; ++ offt += sprintf(arg + offt, "%s", _("locked")); ++ } ++#endif ++#ifdef SHM_HUGETLB ++ if (shmdsp->shm_perm.mode & SHM_HUGETLB) { ++ if (comma) ++ arg[offt++] = ','; ++ offt += sprintf(arg + offt, "%s", _("hugetlb")); ++ } ++#endif ++#ifdef SHM_NORESERVE ++ if (shmdsp->shm_perm.mode & SHM_NORESERVE) { ++ if (comma) ++ arg[offt++] = ','; ++ sprintf(arg + offt, "%s", _("noreserve")); ++ } ++#endif ++ rc = scols_line_refer_data(ln, n, arg); ++ } ++ break; ++ case COL_ATTACH: ++ if (shmdsp->shm_atim != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)shmdsp->shm_atim)); ++ break; ++ case COL_DETACH: ++ if (shmdsp->shm_dtim != 0) ++ rc = scols_line_refer_data(ln, n, ++ make_time(ctl->time_mode, ++ (time_t)shmdsp->shm_dtim)); ++ break; ++ case COL_CPID: ++ xasprintf(&arg, "%u", shmdsp->shm_cprid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_LPID: ++ xasprintf(&arg, "%u", shmdsp->shm_lprid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ case COL_COMMAND: ++ arg = proc_get_command(shmdsp->shm_cprid); ++ rc = scols_line_refer_data(ln, n, arg); ++ break; ++ } ++ if (rc != 0) ++ err(EXIT_FAILURE, _("failed to set data")); ++ arg = NULL; ++ } ++ if (id > -1) ++ break; ++ } ++ ipc_shm_free_info(shmds); ++} ++ ++static void do_shm_global(struct libscols_table *tb) ++{ ++ struct shm_data *shmds, *shmdsp; ++ uint64_t nsegs = 0, sum_segsz = 0; ++ struct ipc_limits lim; ++ ++ ipc_shm_get_limits(&lim); ++ ++ if (ipc_shm_get_info(-1, &shmds) > 0) { ++ for (shmdsp = shmds; shmdsp->next != NULL; shmdsp = shmdsp->next) { ++ ++nsegs; ++ sum_segsz += shmdsp->shm_segsz; ++ } ++ ipc_shm_free_info(shmds); ++ } ++ ++ global_set_data(tb, "SHMMNI", _("Shared memory segments"), nsegs, lim.shmmni, 1); ++ global_set_data(tb, "SHMALL", _("Shared memory pages"), sum_segsz / getpagesize(), lim.shmall, 1); ++ global_set_data(tb, "SHMMAX", _("Max size of shared memory segment (bytes)"), 0, lim.shmmax, 0); ++ global_set_data(tb, "SHMMIN", _("Min size of shared memory segment (bytes)"), 0, lim.shmmin, 0); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int opt, msg = 0, sem = 0, shm = 0, id = -1; ++ int show_time = 0, show_creat = 0, global = 0; ++ size_t i; ++ struct lsipc_control *ctl = xcalloc(1, sizeof(struct lsipc_control)); ++ static struct libscols_table *tb; ++ char *outarg = NULL; ++ ++ /* long only options. */ ++ enum { ++ OPT_NOTRUNC = CHAR_MAX + 1, ++ OPT_NOHEAD, ++ OPT_TIME_FMT ++ }; ++ ++ static const struct option longopts[] = { ++ { "bytes", no_argument, 0, 'b' }, ++ { "creator", no_argument, 0, 'c' }, ++ { "export", no_argument, 0, 'e' }, ++ { "global", no_argument, 0, 'g' }, ++ { "help", no_argument, 0, 'h' }, ++ { "id", required_argument, 0, 'i' }, ++ { "list", no_argument, 0, 'l' }, ++ { "newline", no_argument, 0, 'n' }, ++ { "noheadings", no_argument, 0, OPT_NOHEAD }, ++ { "notruncate", no_argument, 0, OPT_NOTRUNC }, ++ { "numeric-perms", no_argument, 0, 'P' }, ++ { "output", required_argument, 0, 'o' }, ++ { "pid", no_argument, 0, 'p' }, ++ { "queues", no_argument, 0, 'q' }, ++ { "raw", no_argument, 0, 'r' }, ++ { "semaphores", no_argument, 0, 's' }, ++ { "shmems", no_argument, 0, 'm' }, ++ { "time", no_argument, 0, 't' }, ++ { "time-format", required_argument, 0, OPT_TIME_FMT }, ++ { "version", no_argument, 0, 'V' }, ++ {NULL, 0, NULL, 0} ++ }; ++ ++ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ ++ { 'J', 'e', 'l', 'n', 'r' }, ++ { 'g', 'i' }, ++ { 'c', 'o', 't' }, ++ { 'm', 'q', 's' }, ++ { 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 = 0; ++ ++ scols_init_debug(0); ++ ++ while ((opt = getopt_long(argc, argv, "bceghi:lmno:PqrstuV", longopts, NULL)) != -1) { ++ ++ err_exclusive_options(opt, longopts, excl, excl_st); ++ ++ switch (opt) { ++ case 'b': ++ ctl->bytes = 1; ++ break; ++ case 'i': ++ id = strtos32_or_err(optarg, _("failed to parse IPC identifier")); ++ break; ++ case 'e': ++ ctl->outmode = OUT_EXPORT; ++ break; ++ case 'r': ++ ctl->outmode = OUT_RAW; ++ break; ++ case 'o': ++ outarg = optarg; ++ break; ++ case 'g': ++ global = 1; ++ break; ++ case 'q': ++ msg = 1; ++ add_column(columns, ncolumns++, COL_KEY); ++ add_column(columns, ncolumns++, COL_ID); ++ add_column(columns, ncolumns++, COL_PERMS); ++ add_column(columns, ncolumns++, COL_OWNER); ++ add_column(columns, ncolumns++, COL_USEDBYTES); ++ add_column(columns, ncolumns++, COL_MSGS); ++ add_column(columns, ncolumns++, COL_LSPID); ++ add_column(columns, ncolumns++, COL_LRPID); ++ LOWER = COLDESC_IDX_MSG_FIRST; ++ UPPER = COLDESC_IDX_MSG_LAST; ++ break; ++ case 'l': ++ ctl->outmode = OUT_LIST; ++ break; ++ case 'm': ++ shm = 1; ++ add_column(columns, ncolumns++, COL_KEY); ++ add_column(columns, ncolumns++, COL_ID); ++ add_column(columns, ncolumns++, COL_PERMS); ++ add_column(columns, ncolumns++, COL_OWNER); ++ add_column(columns, ncolumns++, COL_SIZE); ++ add_column(columns, ncolumns++, COL_NATTCH); ++ add_column(columns, ncolumns++, COL_STATUS); ++ add_column(columns, ncolumns++, COL_CTIME); ++ add_column(columns, ncolumns++, COL_CPID); ++ add_column(columns, ncolumns++, COL_LPID); ++ add_column(columns, ncolumns++, COL_COMMAND); ++ LOWER = COLDESC_IDX_SHM_FIRST; ++ UPPER = COLDESC_IDX_SHM_LAST; ++ break; ++ case 'n': ++ ctl->outmode = OUT_NEWLINE; ++ break; ++ case 'P': ++ ctl->numperms = 1; ++ break; ++ case 's': ++ sem = 1; ++ add_column(columns, ncolumns++, COL_KEY); ++ add_column(columns, ncolumns++, COL_ID); ++ add_column(columns, ncolumns++, COL_PERMS); ++ add_column(columns, ncolumns++, COL_OWNER); ++ add_column(columns, ncolumns++, COL_NSEMS); ++ LOWER = COLDESC_IDX_SEM_FIRST; ++ UPPER = COLDESC_IDX_SEM_LAST; ++ break; ++ case OPT_NOTRUNC: ++ ctl->notrunc = 1; ++ break; ++ case OPT_NOHEAD: ++ ctl->noheadings = 1; ++ break; ++ case OPT_TIME_FMT: ++ ctl->time_mode = parse_time_mode(optarg); ++ break; ++ case 't': ++ show_time = 1; ++ break; ++ case 'c': ++ show_creat = 1; ++ break; ++ case 'h': ++ usage(stdout); ++ case 'V': ++ printf(UTIL_LINUX_VERSION); ++ return EXIT_SUCCESS; ++ default: ++ usage(stderr); ++ } ++ } ++ ++ /* default is global */ ++ if (msg + shm + sem == 0) { ++ msg = shm = sem = global = 1; ++ if (show_time || show_creat || id != -1) ++ errx(EXIT_FAILURE, _("--global is mutually exclusive with --creator, --id and --time")); ++ } ++ if (global) { ++ add_column(columns, ncolumns++, COL_RESOURCE); ++ add_column(columns, ncolumns++, COL_DESC); ++ add_column(columns, ncolumns++, COL_LIMIT); ++ add_column(columns, ncolumns++, COL_USED); ++ add_column(columns, ncolumns++, COL_USEPERC); ++ LOWER = COLDESC_IDX_SUM_FIRST; ++ UPPER = COLDESC_IDX_SUM_LAST; ++ } ++ ++ /* default to pretty-print if --id specified */ ++ if (id != -1 && !ctl->outmode) ++ ctl->outmode = OUT_PRETTY; ++ ++ if (!ctl->time_mode) ++ ctl->time_mode = ctl->outmode == OUT_PRETTY ? TIME_FULL : TIME_SHORT; ++ ++ if (ctl->outmode == OUT_PRETTY && !(optarg || show_creat || show_time)) { ++ /* all columns for lsipc --<RESOURCE> --id <ID> */ ++ for (ncolumns = 0, i = 0; i < ARRAY_SIZE(coldescs); i++) ++ columns[ncolumns++] = i; ++ } else { ++ if (show_creat) { ++ add_column(columns, ncolumns++, COL_CUID); ++ add_column(columns, ncolumns++, COL_CGID); ++ add_column(columns, ncolumns++, COL_UID); ++ add_column(columns, ncolumns++, COL_GID); ++ } ++ if (msg && show_time) { ++ add_column(columns, ncolumns++, COL_SEND); ++ add_column(columns, ncolumns++, COL_RECV); ++ add_column(columns, ncolumns++, COL_CTIME); ++ } ++ if (shm && show_time) { ++ /* keep "COMMAND" as last column */ ++ size_t cmd = columns[ncolumns - 1] == COL_COMMAND; ++ ++ if (cmd) ++ ncolumns--; ++ add_column(columns, ncolumns++, COL_ATTACH); ++ add_column(columns, ncolumns++, COL_DETACH); ++ if (cmd) ++ add_column(columns, ncolumns++, COL_COMMAND); ++ } ++ if (sem && show_time) { ++ add_column(columns, ncolumns++, COL_OTIME); ++ add_column(columns, ncolumns++, COL_CTIME); ++ } ++ } ++ ++ if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), ++ (int *) &ncolumns, column_name_to_id) < 0) ++ return EXIT_FAILURE; ++ ++ tb = setup_table(ctl); ++ if (!tb) ++ return EXIT_FAILURE; ++ ++ if (msg) { ++ if (global) ++ do_msg_global(tb); ++ else ++ do_msg(id, ctl, tb); ++ } ++ if (shm) { ++ if (global) ++ do_shm_global(tb); ++ else ++ do_shm(id, ctl, tb); ++ } ++ if (sem) { ++ if (global) ++ do_sem_global(tb); ++ else ++ do_sem(id, ctl, tb); ++ } ++ ++ print_table(ctl, tb); ++ ++ scols_unref_table(tb); ++ free(ctl); ++ ++ return EXIT_SUCCESS; ++} ++ +diff --git a/tests/functions.sh b/tests/functions.sh +index 123f6c3..b2d493c 100644 +--- a/tests/functions.sh ++++ b/tests/functions.sh +@@ -50,16 +50,25 @@ function ts_skip_nonroot { + } + + function ts_failed_subtest { ++ local msg="FAILED" ++ local ret=1 ++ if [ "$TS_KNOWN_FAIL" = "yes" ]; then ++ msg="KNOWN FAILED" ++ ret=0 ++ fi ++ + if [ x"$1" == x"" ]; then +- echo " FAILED ($TS_NS)" ++ echo " $msg ($TS_NS)" + else +- echo " FAILED ($1)" ++ echo " $msg ($1)" + fi ++ ++ return $ret + } + + function ts_failed { + ts_failed_subtest "$1" +- exit 1 ++ exit $? + } + + function ts_ok_subtest { +@@ -150,6 +159,7 @@ function ts_init_env { + TS_SUBDIR=$(dirname $TS_SCRIPT) + TS_TESTNAME=$(basename $TS_SCRIPT) + TS_COMPONENT=$(basename $TS_SUBDIR) ++ TS_KNOWN_FAIL="no" + + TS_NSUBTESTS=0 + TS_NSUBFAILED=0 +diff --git a/tests/ts/ipcs/limits2 b/tests/ts/ipcs/limits2 +index f99a354..63f834d 100755 +--- a/tests/ts/ipcs/limits2 ++++ b/tests/ts/ipcs/limits2 +@@ -16,15 +16,20 @@ + # GNU General Public License for more details. + # + +-TS_TOPDIR="$(dirname $0)/../.." ++TS_TOPDIR="${0%/*}/../.." + TS_DESC="basic limits" + + . $TS_TOPDIR/functions.sh + ts_init "$*" +-type bc >/dev/null 2>&1 || ts_skip "cannot find bc command" + + . $TS_SELF/functions.sh + ++# TODO https://github.com/karelzak/util-linux/issues/51 ++SHMALL=$(</proc/sys/kernel/shmall) ++if [ $(bc <<<"(2^64 / $PAGE_SIZE) <= $SHMALL") -eq 1 ]; then ++ TS_KNOWN_FAIL="yes" ++fi ++ + ts_log "check for difference between kernel and IPC" + ipcs_limits_check >> $TS_OUTPUT + +-- +2.7.4 + diff --git a/SOURCES/0079-blkdiscard-backport-zeroout-support.patch b/SOURCES/0079-blkdiscard-backport-zeroout-support.patch new file mode 100644 index 0000000..22edece --- /dev/null +++ b/SOURCES/0079-blkdiscard-backport-zeroout-support.patch @@ -0,0 +1,278 @@ +From ca291153ff2c696696c1406aca6433aab6e412a1 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 24 Jun 2016 13:36:32 +0200 +Subject: [PATCH 79/84] blkdiscard: backport --zeroout support + +The patch also includes upstream cleanups. + +Upstream: http://github.com/karelzak/util-linux/commit/0e765365798c54d412e355798ad584a52035f228 +Upstream: http://github.com/karelzak/util-linux/commit/a3e91e26467a0f644ee568bb0b3d481591834015 +Upstream: http://github.com/karelzak/util-linux/commit/eeae448805c0eb2ef130a6ac301750706bb80420 +Upstream: http://github.com/karelzak/util-linux/commit/7154cc892688f3c58cbbcdc2055f2635c1d0ef5b +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1327886 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + bash-completion/blkdiscard | 2 +- + sys-utils/blkdiscard.8 | 7 +-- + sys-utils/blkdiscard.c | 122 ++++++++++++++++++++++++++++++++++----------- + 3 files changed, 98 insertions(+), 33 deletions(-) + +diff --git a/bash-completion/blkdiscard b/bash-completion/blkdiscard +index 310cdfb..fb3cb1e 100644 +--- a/bash-completion/blkdiscard ++++ b/bash-completion/blkdiscard +@@ -15,7 +15,7 @@ _blkdiscard_module() + esac + case $cur in + -*) +- OPTS="--offset --length --secure --verbose --help --version" ++ OPTS="--offset --length --secure --zeroout --verbose --help --version" + COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) + return 0 + ;; +diff --git a/sys-utils/blkdiscard.8 b/sys-utils/blkdiscard.8 +index 5e094d4..71180e9 100644 +--- a/sys-utils/blkdiscard.8 ++++ b/sys-utils/blkdiscard.8 +@@ -1,15 +1,13 @@ +-.\" -*- nroff -*- + .TH BLKDISCARD 8 "July 2014" "util-linux" "System Administration" + .SH NAME + blkdiscard \- discard sectors on a device + .SH SYNOPSIS + .B blkdiscard ++[options] + .RB [ \-o + .IR offset ] + .RB [ \-l + .IR length ] +-.RB [ \-s ] +-.RB [ \-v ] + .I device + .SH DESCRIPTION + .B blkdiscard +@@ -59,6 +57,9 @@ 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 \-z , " \-\-zeroout" ++Zero-fill rather than discard. ++.TP + .BR \-v , " \-\-verbose" + Display the aligned values of + .I offset +diff --git a/sys-utils/blkdiscard.c b/sys-utils/blkdiscard.c +index 92ca52a..0ba99ee 100644 +--- a/sys-utils/blkdiscard.c ++++ b/sys-utils/blkdiscard.c +@@ -44,43 +44,95 @@ + #include "closestream.h" + + #ifndef BLKDISCARD +-#define BLKDISCARD _IO(0x12,119) ++# define BLKDISCARD _IO(0x12,119) + #endif + + #ifndef BLKSECDISCARD +-#define BLKSECDISCARD _IO(0x12,125) ++# 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]); ++#ifndef BLKZEROOUT ++# define BLKZEROOUT _IO(0x12,127) ++#endif ++ ++enum { ++ ACT_DISCARD = 0, /* default */ ++ ACT_ZEROOUT, ++ ACT_SECURE ++}; ++ ++/* RHEL: backport from upstream lib/monotonic.c */ ++static int gettime_monotonic(struct timeval *tv) ++{ ++#ifdef CLOCK_MONOTONIC ++ /* Can slew only by ntp and adjtime */ ++ int ret; ++ struct timespec ts; ++ ++# ifdef CLOCK_MONOTONIC_RAW ++ /* Linux specific, can't slew */ ++ if (!(ret = clock_gettime(CLOCK_MONOTONIC_RAW, &ts))) { ++# else ++ if (!(ret = clock_gettime(CLOCK_MONOTONIC, &ts))) { ++# endif ++ tv->tv_sec = ts.tv_sec; ++ tv->tv_usec = ts.tv_nsec / 1000; ++ } ++ return ret; ++#else ++ return gettimeofday(tv, NULL); ++#endif ++} ++ ++static void print_stats(int act, char *path, uint64_t stats[]) ++{ ++ switch (act) { ++ case ACT_ZEROOUT: ++ printf(_("%s: Zero-filled %" PRIu64 " bytes from the offset %" PRIu64"\n"), \ ++ path, stats[1], stats[0]); ++ break; ++ case ACT_SECURE: ++ case ACT_DISCARD: ++ printf(_("%s: Discarded %" PRIu64 " bytes from the offset %" PRIu64"\n"), \ ++ path, stats[1], stats[0]); ++ break; ++ } ++} + + static void __attribute__((__noreturn__)) usage(FILE *out) + { + fputs(USAGE_HEADER, out); + fprintf(out, + _(" %s [options] <device>\n"), program_invocation_short_name); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Discard the content of sectors on a device.\n"), out); ++ + fputs(USAGE_OPTIONS, out); +- fputs(_(" -o, --offset <num> offset in bytes to discard from\n" +- " -l, --length <num> length of bytes to discard from the offset\n" +- " -p, --step <num> size of the discard iterations within the offset\n" +- " -s, --secure perform secure discard\n" +- " -v, --verbose print aligned length and offset\n"), +- out); ++ fputs(_(" -o, --offset <num> offset in bytes to discard from\n"), out); ++ fputs(_(" -l, --length <num> length of bytes to discard from the offset\n"), out); ++ fputs(_(" -p, --step <num> size of the discard iterations within the offset\n"), out); ++ fputs(_(" -s, --secure perform secure discard\n"), out); ++ fputs(_(" -z, --zeroout zero-fill rather than discard\n"), out); ++ fputs(_(" -v, --verbose print aligned length and offset\n"), out); ++ + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); + fputs(USAGE_VERSION, out); ++ + fprintf(out, USAGE_MAN_TAIL("blkdiscard(8)")); + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); + } + ++ + int main(int argc, char **argv) + { + char *path; +- int c, fd, verbose = 0, secure = 0, secsize; ++ int c, fd, verbose = 0, secsize; + uint64_t end, blksize, step, range[2], stats[2]; + struct stat sb; +- struct timespec now, last; ++ struct timeval now, last; ++ int act = ACT_DISCARD; + + static const struct option longopts[] = { + { "help", 0, 0, 'h' }, +@@ -90,6 +142,7 @@ int main(int argc, char **argv) + { "step", 1, 0, 'p' }, + { "secure", 0, 0, 's' }, + { "verbose", 0, 0, 'v' }, ++ { "zeroout", 0, 0, 'z' }, + { NULL, 0, 0, 0 } + }; + +@@ -102,7 +155,7 @@ int main(int argc, char **argv) + range[1] = ULLONG_MAX; + step = 0; + +- while ((c = getopt_long(argc, argv, "hVsvo:l:p:", longopts, NULL)) != -1) { ++ while ((c = getopt_long(argc, argv, "hVsvo:l:p:z", longopts, NULL)) != -1) { + switch(c) { + case 'h': + usage(stdout); +@@ -123,11 +176,14 @@ int main(int argc, char **argv) + _("failed to parse step")); + break; + case 's': +- secure = 1; ++ act = ACT_SECURE; + break; + case 'v': + verbose = 1; + break; ++ case 'z': ++ act = ACT_ZEROOUT; ++ break; + default: + usage(stderr); + break; +@@ -149,7 +205,7 @@ int main(int argc, char **argv) + err(EXIT_FAILURE, _("cannot open %s"), path); + + if (fstat(fd, &sb) == -1) +- err(EXIT_FAILURE, _("stat failed %s"), path); ++ err(EXIT_FAILURE, _("stat of %s failed"), path); + if (!S_ISBLK(sb.st_mode)) + errx(EXIT_FAILURE, _("%s: not a block device"), path); + +@@ -178,35 +234,43 @@ int main(int argc, char **argv) + "to sector size %i"), path, range[1], secsize); + + stats[0] = range[0], stats[1] = 0; +- clock_gettime(CLOCK_MONOTONIC, &last); ++ gettime_monotonic(&last); + +- for (range[0] = range[0]; range[0] < end; range[0] += range[1]) { ++ for (/* nothing */; range[0] < end; range[0] += range[1]) { + if (range[0] + range[1] > end) + range[1] = end - range[0]; + +- if (secure) { ++ switch (act) { ++ case ACT_ZEROOUT: ++ if (ioctl(fd, BLKZEROOUT, &range)) ++ err(EXIT_FAILURE, _("%s: BLKZEROOUT ioctl failed"), path); ++ break; ++ case ACT_SECURE: + if (ioctl(fd, BLKSECDISCARD, &range)) + err(EXIT_FAILURE, _("%s: BLKSECDISCARD ioctl failed"), path); +- } else { ++ break; ++ case ACT_DISCARD: + if (ioctl(fd, BLKDISCARD, &range)) + err(EXIT_FAILURE, _("%s: BLKDISCARD ioctl failed"), path); ++ break; + } + +- /* reporting progress */ ++ stats[1] += range[1]; ++ ++ /* reporting progress at most once per second */ + 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; ++ gettime_monotonic(&now); ++ if (now.tv_sec > last.tv_sec && ++ (now.tv_usec >= last.tv_usec || now.tv_sec > last.tv_sec + 1)) { ++ print_stats(act, path, stats); ++ stats[0] += stats[1], stats[1] = 0; + last = now; + } + } +- +- stats[1] += range[1]; + } + +- if (verbose) +- print_stats(path, stats); ++ if (verbose && stats[1]) ++ print_stats(act, path, stats); + + close(fd); + return EXIT_SUCCESS; +-- +2.7.4 + diff --git a/SOURCES/0080-sulogin-and-agetty-virtual-consoles-support-xvc-and-.patch b/SOURCES/0080-sulogin-and-agetty-virtual-consoles-support-xvc-and-.patch new file mode 100644 index 0000000..ae2bc6b --- /dev/null +++ b/SOURCES/0080-sulogin-and-agetty-virtual-consoles-support-xvc-and-.patch @@ -0,0 +1,125 @@ +From 46537bc28d48acb9ae5cac76535262e6b2ec48a2 Mon Sep 17 00:00:00 2001 +From: Werner Fink <werner@suse.de> +Date: Thu, 8 May 2014 12:09:25 +0200 +Subject: [PATCH 80/84] sulogin: (and agetty) virtual consoles support, xvc and + hvc device + +For this approach do not use the ioctl TIOCMGET anymore as this +is for real serial lines only. But switch over to use the ioctl +KDGKBMODE as this is unique to the virtual console lines only. + +Upstream: http://github.com/karelzak/util-linux/commit/b9c7390948c7850db2bee82ad64624930962cc14 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1029385 +Signed-off-by: Werner Fink <werner@suse.de> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + login-utils/sulogin.c | 17 ++++++++++++----- + term-utils/agetty.c | 23 +++++++++++------------ + 2 files changed, 23 insertions(+), 17 deletions(-) + +diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c +index 32ae9a2..bbd67b3 100644 +--- a/login-utils/sulogin.c ++++ b/login-utils/sulogin.c +@@ -49,6 +49,11 @@ + # include <selinux/get_context_list.h> + #endif + ++#ifdef __linux__ ++# include <sys/kd.h> ++# include <sys/param.h> ++#endif ++ + #include "c.h" + #include "closestream.h" + #include "nls.h" +@@ -93,10 +98,14 @@ static void tcinit(struct console *con) + return; + } + +- /* Handle serial lines here */ +- if (ioctl(fd, TIOCMGET, (char *) &mode) == 0) { ++ /* Handle lines other than virtual consoles here */ ++#if defined(KDGKBMODE) ++ if (ioctl(fd, KDGKBMODE, &mode) < 0) ++#endif ++ { + speed_t ispeed, ospeed; + struct winsize ws; ++ errno = 0; + + /* this is a modem line */ + con->flags |= CON_SERIAL; +@@ -142,9 +151,7 @@ static void tcinit(struct console *con) + goto setattr; + } + #if defined(IUTF8) && defined(KDGKBMODE) +- /* Detect mode of current keyboard setup, e.g. for UTF-8 */ +- if (ioctl(fd, KDGKBMODE, &mode) < 0) +- mode = K_RAW; ++ /* Handle mode of current keyboard setup, e.g. for UTF-8 */ + switch(mode) { + case K_UNICODE: + setlocale(LC_CTYPE, "C.UTF-8"); +diff --git a/term-utils/agetty.c b/term-utils/agetty.c +index c7af154..5692126 100644 +--- a/term-utils/agetty.c ++++ b/term-utils/agetty.c +@@ -134,6 +134,7 @@ struct options { + int nice; /* Run login with this priority */ + int numspeed; /* number of baud rates to try */ + int clocal; /* CLOCAL_MODE_* */ ++ int kbmode; /* Keyboard mode if virtual console */ + speed_t speeds[MAX_SPEED]; /* baud rates to be tried */ + }; + +@@ -886,7 +887,7 @@ static void update_utmp(struct options *op) + static void open_tty(char *tty, struct termios *tp, struct options *op) + { + const pid_t pid = getpid(); +- int serial, closed = 0; ++ int closed = 0; + + /* Set up new standard input, unless we are given an already opened port. */ + +@@ -1016,15 +1017,18 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) + + /* + * Detect if this is a virtual console or serial/modem line. +- * In case of a virtual console the ioctl TIOCMGET fails and +- * the error number will be set to EINVAL. ++ * In case of a virtual console the ioctl KDGKBMODE succeeds ++ * whereas on other lines it will fails. + */ +- if (ioctl(STDIN_FILENO, TIOCMGET, &serial) < 0 && (errno == EINVAL)) { ++ if (ioctl(STDIN_FILENO, KDGKBMODE, &op->kbmode) == 0) { + op->flags |= F_VCONSOLE; + if (!op->term) + op->term = DEFAULT_VCTERM; +- } else if (!op->term) +- op->term = DEFAULT_STERM; ++ } else { ++ op->kbmode = K_RAW; ++ if (!op->term) ++ op->term = DEFAULT_STERM; ++ } + + setenv("TERM", op->term, 1); + } +@@ -1037,12 +1041,7 @@ static void termio_init(struct options *op, struct termios *tp) + + if (op->flags & F_VCONSOLE) { + #if defined(IUTF8) && defined(KDGKBMODE) +- int mode; +- +- /* Detect mode of current keyboard setup, e.g. for UTF-8 */ +- if (ioctl(STDIN_FILENO, KDGKBMODE, &mode) < 0) +- mode = K_RAW; +- switch(mode) { ++ switch(op->kbmode) { + case K_UNICODE: + setlocale(LC_CTYPE, "C.UTF-8"); + op->flags |= F_UTF8; +-- +2.7.4 + diff --git a/SOURCES/0081-chrt-backport-DEADLINE-scheduler-support.patch b/SOURCES/0081-chrt-backport-DEADLINE-scheduler-support.patch new file mode 100644 index 0000000..fda445b --- /dev/null +++ b/SOURCES/0081-chrt-backport-DEADLINE-scheduler-support.patch @@ -0,0 +1,791 @@ +From 674769893bb8d5f1991c6a3e5d96337b37aeb86f Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 27 Jun 2016 14:14:28 +0200 +Subject: [PATCH 81/86] chrt: backport DEADLINE scheduler support + +Backport upstream commits: + +2e31d1c chrt: validate priority before trying to use it +b3a5067 chrt: make --sched-* short options to require an argument +a03eac5 chrt: restore removed ifdef SCHED_RESET_ON_FORK +59e4a38 chrt: fix case SCHED_RR +acde3a0 chrt: use sched_getattr() +1a7e639 chrt: add support for SCHED_DEADLINE +1516758 chrt: use sched_setattr() if available +a6fec53 chrt: make usage more readable +4820a73 chrt: set function refactoring +a30cf65 chrt: output function refactoring +7a4ea56 chrt: add control struct +9acbe2a chrt: slice up the usage text and normalize its layout +4e4bc0c chrt: make the usage synopsis clearer +3fabc36 chrt: fix --help inconsistency +451dbcf textual: add a docstring to most of the utilities +a587cc5 textual: use manual tail usage() macro +f627750 textual: use version printing macro everywhere +a7560c0 textual: make the license of chrt and taskset slightly more explicit +4ce393f textual: fix several typos and angular brackets in messages +6f27e44 chrt: add fallback to be usable on kernels without sched_{get,set}attr + +* Fri Jul 01 2016 re-spin [kzak]: +- add fallback for old glibc-headers without SYS_sched_{set,get}attr + +* Tue Jul 12 016 re-spin [kzak]: +- add runtime fallback for systems without sched_{get,set}attr syscalls + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1298384 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + configure.ac | 17 +- + m4/ul.m4 | 8 - + schedutils/chrt.c | 547 ++++++++++++++++++++++++++++++++++++++---------------- + 3 files changed, 400 insertions(+), 172 deletions(-) + +diff --git a/configure.ac b/configure.ac +index fe0a011..266ef08 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -338,6 +338,8 @@ AC_CHECK_FUNCS([ \ + scandirat \ + setresgid \ + setresuid \ ++ sched_setattr \ ++ sched_setscheduler \ + sigqueue \ + srandom \ + strnchr \ +@@ -1383,6 +1385,20 @@ UL_REQUIRES_SYSCALL_CHECK([taskset], + AM_CONDITIONAL(BUILD_TASKSET, test "x$build_taskset" = xyes) + + ++have_schedsetter=no ++AS_IF([test "x$ac_cv_func_sched_setscheduler" = xyes], [have_schedsetter=yes], ++ [test "x$ac_cv_func_sched_setattr" = xyes], [have_schedsetter=yes]) ++ ++UL_BUILD_INIT([chrt], [check]) ++UL_REQUIRES_BUILD([chrt], [schedutils]) ++UL_REQUIRES_HAVE([chrt], [schedsetter], [sched_set functions]) ++AM_CONDITIONAL([BUILD_CHRT], [test "x$build_chrt" = xyes]) ++ ++AS_IF([test "x$build_chrt" = xyes], [ ++ UL_CHECK_SYSCALL([sched_setattr]) ++]) ++ ++ + AC_ARG_ENABLE([wall], + AS_HELP_STRING([--disable-wall], [do not build wall]), + [], enable_wall=yes +@@ -1562,7 +1578,6 @@ AC_ARG_VAR([SOLIB_LDFLAGS], + + LIBS="" + +- + AC_CONFIG_HEADERS(config.h) + + # +diff --git a/m4/ul.m4 b/m4/ul.m4 +index c0082d0..db44589 100644 +--- a/m4/ul.m4 ++++ b/m4/ul.m4 +@@ -92,7 +92,6 @@ AC_DEFUN([UL_CHECK_SYSCALL], [ + ]) + ul_cv_syscall_$1=$syscall + ]) +- AM_CONDITIONAL([HAVE_]m4_toupper($1), [test "x$ul_cv_syscall_$1" != xno]) + case $ul_cv_syscall_$1 in #( + no) AC_MSG_WARN([Unable to detect syscall $1.]) ;; + SYS_*) ;; +@@ -266,13 +265,6 @@ AC_DEFUN([UL_REQUIRES_SYSCALL_CHECK], [ + m4_define([suffix], m4_default([$4],$1)) + m4_define([callname], m4_default([$3],$1)) + +- dnl This is default, $3 will redefine the condition +- dnl +- dnl TODO: remove this junk, AM_CONDITIONAL should not be used for any HAVE_* +- dnl variables, all we need is BUILD_* only. +- dnl +- AM_CONDITIONAL([HAVE_]m4_toupper(callname), [false]) +- + if test "x$[build_]suffix" != xno; then + if test "x$[enable_]suffix" = xno; then + [build_]suffix=no +diff --git a/schedutils/chrt.c b/schedutils/chrt.c +index 20df6fa..edae0d9 100644 +--- a/schedutils/chrt.c ++++ b/schedutils/chrt.c +@@ -1,13 +1,11 @@ + /* +- * chrt.c - chrt +- * Command-line utility for manipulating a task's real-time attributes ++ * chrt.c - manipulate a task's real-time attributes + * +- * Robert Love <rml@tech9.net> +- * 27-Apr-2002: initial version +- * 04-May-2011: make thread aware - Davidlohr Bueso <dave@gnu.org> ++ * 27-Apr-2002: initial version - Robert Love <rml@tech9.net> ++ * 04-May-2011: make it thread-aware - Davidlohr Bueso <dave@gnu.org> + * + * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License, v2, as ++ * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, +@@ -50,108 +48,260 @@ + # define SCHED_IDLE 5 + #endif + ++/* flag by sched_getscheduler() */ + #if defined(__linux__) && !defined(SCHED_RESET_ON_FORK) +-#define SCHED_RESET_ON_FORK 0x40000000 ++# define SCHED_RESET_ON_FORK 0x40000000 + #endif + ++/* flag by sched_getattr() */ ++#if defined(__linux__) && !defined(SCHED_FLAG_RESET_ON_FORK) ++# define SCHED_FLAG_RESET_ON_FORK 0x01 ++#endif + +-static void __attribute__((__noreturn__)) show_usage(int rc) +-{ +- FILE *out = rc == EXIT_SUCCESS ? stdout : stderr; ++#if defined (__linux__) && !defined(HAVE_SCHED_SETATTR) ++# include <sys/syscall.h> ++#endif + +- fprintf(out, _( +- "\nchrt - manipulate real-time attributes of a process\n" +- "\nSet policy:\n" +- " chrt [options] [-policy] <priority> [-p <pid> | <command> <arg> ...]\n" +- "\nGet policy:\n" +- " chrt [options] -p <pid>\n")); +- +- fprintf(out, _( +- "\nScheduling policies:\n" +- " -b | --batch set policy to SCHED_BATCH\n" +- " -f | --fifo set policy to SCHED_FIFO\n" +- " -i | --idle set policy to SCHED_IDLE\n" +- " -o | --other set policy to SCHED_OTHER\n" +- " -r | --rr set policy to SCHED_RR (default)\n")); ++/* usable kernel-headers, but old glibc-headers */ ++#if defined (__linux__) && !defined(SYS_sched_setattr) && defined(__NR_sched_setattr) ++# define SYS_sched_setattr __NR_sched_setattr ++#endif + +-#ifdef SCHED_RESET_ON_FORK +- fprintf(out, _( +- "\nScheduling flags:\n" +- " -R | --reset-on-fork set SCHED_RESET_ON_FORK for FIFO or RR\n")); ++#if defined (__linux__) && !defined(SYS_sched_getattr) && defined(__NR_sched_getattr) ++# define SYS_sched_getattr __NR_sched_getattr + #endif +- fprintf(out, _( +- "\nOptions:\n" +- " -a | --all-tasks operate on all the tasks (threads) for a given pid\n" +- " -h | --help display this help\n" +- " -m | --max show min and max valid priorities\n" +- " -p | --pid operate on existing given pid\n" +- " -v | --verbose display status information\n" +- " -V | --version output version information\n\n")); + +- exit(rc); ++#if defined (__linux__) && !defined(HAVE_SCHED_SETATTR) && defined(SYS_sched_setattr) ++# define HAVE_SCHED_SETATTR ++ ++struct sched_attr { ++ uint32_t size; ++ uint32_t sched_policy; ++ uint64_t sched_flags; ++ ++ /* SCHED_NORMAL, SCHED_BATCH */ ++ int32_t sched_nice; ++ ++ /* SCHED_FIFO, SCHED_RR */ ++ uint32_t sched_priority; ++ ++ /* SCHED_DEADLINE (nsec) */ ++ uint64_t sched_runtime; ++ uint64_t sched_deadline; ++ uint64_t sched_period; ++}; ++ ++static int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags) ++{ ++ return syscall(SYS_sched_setattr, pid, attr, flags); + } + +-static void show_rt_info(pid_t pid, int isnew) ++static int sched_getattr(pid_t pid, struct sched_attr *attr, unsigned int size, unsigned int flags) + { +- struct sched_param sp; +- int policy; ++ return syscall(SYS_sched_getattr, pid, attr, size, flags); ++} ++#endif + +- /* don't display "pid 0" as that is confusing */ +- if (!pid) +- pid = getpid(); ++/* the SCHED_DEADLINE is supported since Linux 3.14 ++ * commit id aab03e05e8f7e26f51dee792beddcb5cca9215a5 ++ * -- sched_setattr() is required for this policy! ++ */ ++#if defined (__linux__) && !defined(SCHED_DEADLINE) && defined(HAVE_SCHED_SETATTR) ++# define SCHED_DEADLINE 6 ++#endif + +- policy = sched_getscheduler(pid); +- if (policy == -1) +- err(EXIT_FAILURE, _("failed to get pid %d's policy"), pid); ++/* control struct */ ++struct chrt_ctl { ++ pid_t pid; ++ int policy; /* SCHED_* */ ++ int priority; + +- if (isnew) +- printf(_("pid %d's new scheduling policy: "), pid); +- else +- printf(_("pid %d's current scheduling policy: "), pid); ++ uint64_t runtime; /* --sched-* options */ ++ uint64_t deadline; ++ uint64_t period; ++ ++ unsigned int all_tasks : 1, /* all threads of the PID */ ++ reset_on_fork : 1, /* SCHED_RESET_ON_FORK */ ++ altered : 1, /* sched_set**() used */ ++ verbose : 1; /* verbose output */ ++}; ++ ++static void __attribute__((__noreturn__)) show_usage(int rc) ++{ ++ FILE *out = rc == EXIT_SUCCESS ? stdout : stderr; + ++ fputs(_("Show or change the real-time scheduling attributes of a process.\n"), out); ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Set policy:\n" ++ " chrt [options] <priority> <command> [<arg>...]\n" ++ " chrt [options] --pid <priority> <pid>\n"), out); ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Get policy:\n" ++ " chrt [options] -p <pid>\n"), out); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Policy options:\n"), out); ++ fputs(_(" -b, --batch set policy to SCHED_BATCH\n"), out); ++ fputs(_(" -d, --deadline set policy to SCHED_DEADLINE\n"), out); ++ fputs(_(" -f, --fifo set policy to SCHED_FIFO\n"), out); ++ fputs(_(" -i, --idle set policy to SCHED_IDLE\n"), out); ++ fputs(_(" -o, --other set policy to SCHED_OTHER\n"), out); ++ fputs(_(" -r, --rr set policy to SCHED_RR (default)\n"), out); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Scheduling options:\n"), out); ++ fputs(_(" -R, --reset-on-fork set SCHED_RESET_ON_FORK for FIFO or RR\n"), out); ++ fputs(_(" -T, --sched-runtime <ns> runtime parameter for DEADLINE\n"), out); ++ fputs(_(" -P, --sched-period <ns> period parameter for DEADLINE\n"), out); ++ fputs(_(" -D, --sched-deadline <ns> deadline parameter for DEADLINE\n"), out); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Other options:\n"), out); ++ fputs(_(" -a, --all-tasks operate on all the tasks (threads) for a given pid\n"), out); ++ fputs(_(" -m, --max show min and max valid priorities\n"), out); ++ fputs(_(" -p, --pid operate on existing given pid\n"), out); ++ fputs(_(" -v, --verbose display status information\n"), out); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(USAGE_HELP, out); ++ fputs(USAGE_VERSION, out); ++ ++ fprintf(out, USAGE_MAN_TAIL("chrt(1)")); ++ exit(rc); ++} ++ ++static const char *get_policy_name(int policy) ++{ + switch (policy) { + case SCHED_OTHER: +- printf("SCHED_OTHER\n"); +- break; ++ return "SCHED_OTHER"; + case SCHED_FIFO: +- printf("SCHED_FIFO\n"); +- break; + #ifdef SCHED_RESET_ON_FORK + case SCHED_FIFO | SCHED_RESET_ON_FORK: +- printf("SCHED_FIFO|SCHED_RESET_ON_FORK\n"); +- break; + #endif ++ return "SCHED_FIFO"; + #ifdef SCHED_IDLE + case SCHED_IDLE: +- printf("SCHED_IDLE\n"); +- break; ++ return "SCHED_IDLE"; + #endif + case SCHED_RR: +- printf("SCHED_RR\n"); +- break; + #ifdef SCHED_RESET_ON_FORK + case SCHED_RR | SCHED_RESET_ON_FORK: +- printf("SCHED_RR|SCHED_RESET_ON_FORK\n"); +- break; + #endif ++ return "SCHED_RR"; + #ifdef SCHED_BATCH + case SCHED_BATCH: +- printf("SCHED_BATCH\n"); +- break; ++ return "SCHED_BATCH"; ++#endif ++#ifdef SCHED_DEADLINE ++ case SCHED_DEADLINE: ++ return "SCHED_DEADLINE"; + #endif + default: +- warnx(_("unknown scheduling policy")); ++ break; ++ } ++ ++ return _("unknown"); ++} ++ ++static void show_sched_pid_info(struct chrt_ctl *ctl, pid_t pid) ++{ ++ int policy, reset_on_fork = 0, prio = 0; ++#ifdef SCHED_DEADLINE ++ uint64_t deadline = 0, runtime = 0, period = 0; ++#endif ++ ++ /* don't display "pid 0" as that is confusing */ ++ if (!pid) ++ pid = getpid(); ++ ++ errno = 0; ++ ++ /* ++ * New way ++ */ ++#ifdef HAVE_SCHED_SETATTR ++ { ++ struct sched_attr sa; ++ ++ if (sched_getattr(pid, &sa, sizeof(sa), 0) != 0) { ++ if (errno == ENOSYS) ++ goto fallback; ++ err(EXIT_FAILURE, _("failed to get pid %d's policy"), pid); ++ } ++ ++ policy = sa.sched_policy; ++ prio = sa.sched_priority; ++ reset_on_fork = sa.sched_flags & SCHED_FLAG_RESET_ON_FORK; ++ deadline = sa.sched_deadline; ++ runtime = sa.sched_runtime; ++ period = sa.sched_period; + } ++#endif ++ ++ /* ++ * Old way ++ */ ++fallback: ++ if (errno == ENOSYS) { ++ struct sched_param sp; + +- if (sched_getparam(pid, &sp)) +- err(EXIT_FAILURE, _("failed to get pid %d's attributes"), pid); ++ policy = sched_getscheduler(pid); ++ if (policy == -1) ++ err(EXIT_FAILURE, _("failed to get pid %d's policy"), pid); + +- if (isnew) +- printf(_("pid %d's new scheduling priority: %d\n"), +- pid, sp.sched_priority); ++ if (sched_getparam(pid, &sp) != 0) ++ err(EXIT_FAILURE, _("failed to get pid %d's attributes"), pid); ++ else ++ prio = sp.sched_priority; ++# ifdef SCHED_RESET_ON_FORK ++ if (policy == (SCHED_FIFO|SCHED_RESET_ON_FORK) || policy == (SCHED_BATCH|SCHED_RESET_ON_FORK)) ++ reset_on_fork = 1; ++# endif ++ } ++ ++ if (ctl->altered) ++ printf(_("pid %d's new scheduling policy: %s"), pid, get_policy_name(policy)); ++ else ++ printf(_("pid %d's current scheduling policy: %s"), pid, get_policy_name(policy)); ++ ++ if (reset_on_fork) ++ printf("|SCHED_RESET_ON_FORK"); ++ putchar('\n'); ++ ++ if (ctl->altered) ++ printf(_("pid %d's new scheduling priority: %d\n"), pid, prio); + else +- printf(_("pid %d's current scheduling priority: %d\n"), +- pid, sp.sched_priority); ++ printf(_("pid %d's current scheduling priority: %d\n"), pid, prio); ++ ++#ifdef SCHED_DEADLINE ++ if (policy == SCHED_DEADLINE) { ++ if (ctl->altered) ++ printf(_("pid %d's new runtime/deadline/period parameters: %ju/%ju/%ju\n"), ++ pid, runtime, deadline, period); ++ else ++ printf(_("pid %d's current runtime/deadline/period parameters: %ju/%ju/%ju\n"), ++ pid, runtime, deadline, period); ++ } ++#endif ++} ++ ++ ++static void show_sched_info(struct chrt_ctl *ctl) ++{ ++ if (ctl->all_tasks) { ++ pid_t tid; ++ struct proc_tasks *ts = proc_open_tasks(ctl->pid); ++ ++ if (!ts) ++ err(EXIT_FAILURE, _("cannot obtain the list of tasks")); ++ ++ while (!proc_next_tid(ts, &tid)) ++ show_sched_pid_info(ctl, tid); ++ ++ proc_close_tasks(ts); ++ } else ++ show_sched_pid_info(ctl, ctl->pid); + } + + static void show_min_max(void) +@@ -167,52 +317,116 @@ static void show_min_max(void) + #ifdef SCHED_IDLE + SCHED_IDLE, + #endif +- }; +- const char *names[] = { +- "OTHER", +- "FIFO", +- "RR", +-#ifdef SCHED_BATCH +- "BATCH", +-#endif +-#ifdef SCHED_IDLE +- "IDLE", ++#ifdef SCHED_DEADLINE ++ SCHED_DEADLINE, + #endif + }; + + for (i = 0; i < ARRAY_SIZE(policies); i++) { +- int max = sched_get_priority_max(policies[i]); +- int min = sched_get_priority_min(policies[i]); ++ int plc = policies[i]; ++ int max = sched_get_priority_max(plc); ++ int min = sched_get_priority_min(plc); + + if (max >= 0 && min >= 0) +- printf(_("SCHED_%s min/max priority\t: %d/%d\n"), +- names[i], min, max); ++ printf(_("%s min/max priority\t: %d/%d\n"), ++ get_policy_name(plc), min, max); + else +- printf(_("SCHED_%s not supported?\n"), names[i]); ++ printf(_("%s not supported?\n"), get_policy_name(plc)); + } + } + ++static int set_sched_one_by_setscheduler(struct chrt_ctl *ctl, pid_t pid) ++{ ++ struct sched_param sp = { .sched_priority = ctl->priority }; ++ int policy = ctl->policy; ++ ++# ifdef SCHED_RESET_ON_FORK ++ if (ctl->reset_on_fork) ++ policy |= SCHED_RESET_ON_FORK; ++# endif ++ return sched_setscheduler(pid, policy, &sp); ++} ++ ++ ++#ifndef HAVE_SCHED_SETATTR ++static int set_sched_one(struct chrt_ctl *ctl, pid_t pid) ++{ ++ return set_sched_one_by_setscheduler(ctl, pid); ++} ++ ++#else /* !HAVE_SCHED_SETATTR */ ++static int set_sched_one(struct chrt_ctl *ctl, pid_t pid) ++{ ++ /* use main() to check if the setting makes sense */ ++ struct sched_attr sa = { ++ .size = sizeof(struct sched_attr), ++ .sched_policy = ctl->policy, ++ .sched_priority = ctl->priority, ++ .sched_runtime = ctl->runtime, ++ .sched_period = ctl->period, ++ .sched_deadline = ctl->deadline ++ }; ++ int rc; ++ ++# ifdef SCHED_RESET_ON_FORK ++ if (ctl->reset_on_fork) ++ sa.sched_flags |= SCHED_RESET_ON_FORK; ++# endif ++ errno = 0; ++ rc = sched_setattr(pid, &sa, 0); ++ ++ if (rc != 0 && errno == ENOSYS && ctl->policy != SCHED_DEADLINE) ++ /* fallback -- build with new kernel/libc, but executed on old kernels */ ++ rc = set_sched_one_by_setscheduler(ctl, pid); ++ ++ return rc; ++} ++#endif /* HAVE_SCHED_SETATTR */ ++ ++static void set_sched(struct chrt_ctl *ctl) ++{ ++ if (ctl->all_tasks) { ++ pid_t tid; ++ struct proc_tasks *ts = proc_open_tasks(ctl->pid); ++ ++ if (!ts) ++ err(EXIT_FAILURE, _("cannot obtain the list of tasks")); ++ ++ while (!proc_next_tid(ts, &tid)) ++ if (set_sched_one(ctl, tid) == -1) ++ err(EXIT_FAILURE, _("failed to set tid %d's policy"), tid); ++ ++ proc_close_tasks(ts); ++ ++ } else if (set_sched_one(ctl, ctl->pid) == -1) ++ err(EXIT_FAILURE, _("failed to set pid %d's policy"), ctl->pid); ++ ++ ctl->altered = 1; ++} ++ + int main(int argc, char **argv) + { +- int i, policy = SCHED_RR, priority = 0, verbose = 0, policy_flag = 0, +- all_tasks = 0; +- struct sched_param sp; +- pid_t pid = -1; ++ struct chrt_ctl _ctl = { .pid = -1 }, *ctl = &_ctl; ++ int c; + + static const struct option longopts[] = { +- { "all-tasks", 0, NULL, 'a' }, +- { "batch", 0, NULL, 'b' }, +- { "fifo", 0, NULL, 'f' }, +- { "idle", 0, NULL, 'i' }, +- { "pid", 0, NULL, 'p' }, +- { "help", 0, NULL, 'h' }, +- { "max", 0, NULL, 'm' }, +- { "other", 0, NULL, 'o' }, +- { "rr", 0, NULL, 'r' }, +- { "reset-on-fork", 0, NULL, 'R' }, +- { "verbose", 0, NULL, 'v' }, +- { "version", 0, NULL, 'V' }, +- { NULL, 0, NULL, 0 } ++ { "all-tasks", no_argument, NULL, 'a' }, ++ { "batch", no_argument, NULL, 'b' }, ++ { "deadline", no_argument, NULL, 'd' }, ++ { "fifo", no_argument, NULL, 'f' }, ++ { "idle", no_argument, NULL, 'i' }, ++ { "pid", no_argument, NULL, 'p' }, ++ { "help", no_argument, NULL, 'h' }, ++ { "max", no_argument, NULL, 'm' }, ++ { "other", no_argument, NULL, 'o' }, ++ { "rr", no_argument, NULL, 'r' }, ++ { "sched-runtime", required_argument, NULL, 'T' }, ++ { "sched-period", required_argument, NULL, 'P' }, ++ { "sched-deadline", required_argument, NULL, 'D' }, ++ { "reset-on-fork", no_argument, NULL, 'R' }, ++ { "verbose", no_argument, NULL, 'v' }, ++ { "version", no_argument, NULL, 'V' }, ++ { NULL, no_argument, NULL, 0 } + }; + + setlocale(LC_ALL, ""); +@@ -220,51 +434,63 @@ int main(int argc, char **argv) + textdomain(PACKAGE); + atexit(close_stdout); + +- while((i = getopt_long(argc, argv, "+abfiphmoRrvV", longopts, NULL)) != -1) ++ while((c = getopt_long(argc, argv, "+abdD:fiphmoP:T:rRvV", longopts, NULL)) != -1) + { + int ret = EXIT_FAILURE; + +- switch (i) { ++ switch (c) { + case 'a': +- all_tasks = 1; ++ ctl->all_tasks = 1; + break; + case 'b': + #ifdef SCHED_BATCH +- policy = SCHED_BATCH; ++ ctl->policy = SCHED_BATCH; ++#endif ++ break; ++ ++ case 'd': ++#ifdef SCHED_DEADLINE ++ ctl->policy = SCHED_DEADLINE; + #endif + break; + case 'f': +- policy = SCHED_FIFO; ++ ctl->policy = SCHED_FIFO; + break; + case 'R': +-#ifdef SCHED_RESET_ON_FORK +- policy_flag |= SCHED_RESET_ON_FORK; +-#endif ++ ctl->reset_on_fork = 1; + break; + case 'i': + #ifdef SCHED_IDLE +- policy = SCHED_IDLE; ++ ctl->policy = SCHED_IDLE; + #endif + break; + case 'm': + show_min_max(); + return EXIT_SUCCESS; + case 'o': +- policy = SCHED_OTHER; ++ ctl->policy = SCHED_OTHER; + break; + case 'p': + errno = 0; +- pid = strtos32_or_err(argv[argc - 1], _("invalid PID argument")); ++ ctl->pid = strtos32_or_err(argv[argc - 1], _("invalid PID argument")); + break; + case 'r': +- policy = SCHED_RR; ++ ctl->policy = SCHED_RR; + break; + case 'v': +- verbose = 1; ++ ctl->verbose = 1; ++ break; ++ case 'T': ++ ctl->runtime = strtou64_or_err(optarg, _("invalid runtime argument")); ++ break; ++ case 'P': ++ ctl->period = strtou64_or_err(optarg, _("invalid period argument")); ++ break; ++ case 'D': ++ ctl->deadline = strtou64_or_err(optarg, _("invalid deadline argument")); + break; + case 'V': +- printf(_("%s from %s\n"), program_invocation_short_name, +- PACKAGE_STRING); ++ printf(UTIL_LINUX_VERSION); + return EXIT_SUCCESS; + case 'h': + ret = EXIT_SUCCESS; +@@ -274,61 +500,56 @@ int main(int argc, char **argv) + } + } + +- if (((pid > -1) && argc - optind < 1) || +- ((pid == -1) && argc - optind < 2)) ++ if (((ctl->pid > -1) && argc - optind < 1) || ++ ((ctl->pid == -1) && argc - optind < 2)) + show_usage(EXIT_FAILURE); + +- if ((pid > -1) && (verbose || argc - optind == 1)) { +- if (all_tasks) { +- pid_t tid; +- struct proc_tasks *ts = proc_open_tasks(pid); +- +- if (!ts) +- err(EXIT_FAILURE, _("cannot obtain the list of tasks")); +- while (!proc_next_tid(ts, &tid)) +- show_rt_info(tid, FALSE); +- proc_close_tasks(ts); +- } else +- show_rt_info(pid, FALSE); +- ++ if ((ctl->pid > -1) && (ctl->verbose || argc - optind == 1)) { ++ show_sched_info(ctl); + if (argc - optind == 1) + return EXIT_SUCCESS; + } + + errno = 0; +- priority = strtos32_or_err(argv[optind], _("invalid priority argument")); ++ ctl->priority = strtos32_or_err(argv[optind], _("invalid priority argument")); + + #ifdef SCHED_RESET_ON_FORK +- /* sanity check */ +- if ((policy_flag & SCHED_RESET_ON_FORK) && +- !(policy == SCHED_FIFO || policy == SCHED_RR)) +- errx(EXIT_FAILURE, _("SCHED_RESET_ON_FORK flag is supported for " ++ if (ctl->reset_on_fork && ctl->policy != SCHED_FIFO && ctl->policy != SCHED_RR) ++ errx(EXIT_FAILURE, _("--reset-on-fork option is supported for " + "SCHED_FIFO and SCHED_RR policies only")); + #endif +- +- policy |= policy_flag; +- +- if (pid == -1) +- pid = 0; +- sp.sched_priority = priority; +- +- if (all_tasks) { +- pid_t tid; +- struct proc_tasks *ts = proc_open_tasks(pid); +- +- if (!ts) +- err(EXIT_FAILURE, _("cannot obtain the list of tasks")); +- while (!proc_next_tid(ts, &tid)) +- if (sched_setscheduler(tid, policy, &sp) == -1) +- err(EXIT_FAILURE, _("failed to set tid %d's policy"), tid); +- proc_close_tasks(ts); +- } else if (sched_setscheduler(pid, policy, &sp) == -1) +- err(EXIT_FAILURE, _("failed to set pid %d's policy"), pid); +- +- if (verbose) +- show_rt_info(pid, TRUE); +- +- if (!pid) { ++#ifdef SCHED_DEADLINE ++ if ((ctl->runtime || ctl->deadline || ctl->period) && ctl->policy != SCHED_DEADLINE) ++ errx(EXIT_FAILURE, _("--sched-{runtime,deadline,period} options " ++ "are supported for SCHED_DEADLINE only")); ++ if (ctl->policy == SCHED_DEADLINE) { ++ /* The basic rule is runtime <= deadline <= period, so we can ++ * make deadline and runtime optional on command line. Note we ++ * don't check any values or set any defaults, it's kernel ++ * responsibility. ++ */ ++ if (ctl->deadline == 0) ++ ctl->deadline = ctl->period; ++ if (ctl->runtime == 0) ++ ctl->runtime = ctl->deadline; ++ } ++#else ++ if (ctl->runtime || ctl->deadline || ctl->period) ++ errx(EXIT_FAILURE, _("SCHED_DEADLINE is unsupported")); ++#endif ++ if (ctl->pid == -1) ++ ctl->pid = 0; ++ if (ctl->priority < sched_get_priority_min(ctl->policy) || ++ sched_get_priority_max(ctl->policy) < ctl->priority) ++ errx(EXIT_FAILURE, ++ _("unsupported priority value for the policy: %d: see --max for valid range"), ++ ctl->priority); ++ set_sched(ctl); ++ ++ if (ctl->verbose) ++ show_sched_info(ctl); ++ ++ if (!ctl->pid) { + argv += optind + 1; + execvp(argv[0], argv); + err(EXIT_FAILURE, _("failed to execute %s"), argv[0]); +-- +2.7.4 + diff --git a/SOURCES/0082-fdisk-backport-DOS-logical-partitions-chain-reorder.patch b/SOURCES/0082-fdisk-backport-DOS-logical-partitions-chain-reorder.patch new file mode 100644 index 0000000..23b1f47 --- /dev/null +++ b/SOURCES/0082-fdisk-backport-DOS-logical-partitions-chain-reorder.patch @@ -0,0 +1,211 @@ +From 28b08b639aeaadbfcc3fb66558e6b392b2b5d44c Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 28 Jun 2016 11:30:21 +0200 +Subject: [PATCH 82/86] fdisk: backport DOS logical partitions chain reorder + +... from the current upstream. + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1304246 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + fdisks/fdiskdoslabel.c | 170 +++++++++++++++++++++++++++++++++---------------- + 1 file changed, 116 insertions(+), 54 deletions(-) + +diff --git a/fdisks/fdiskdoslabel.c b/fdisks/fdiskdoslabel.c +index fe04ac7..b7eb35a 100644 +--- a/fdisks/fdiskdoslabel.c ++++ b/fdisks/fdiskdoslabel.c +@@ -55,6 +55,22 @@ static int MBRbuffer_changed; + #define cround(c, n) (fdisk_context_use_cylinders(c) ? \ + ((n) / fdisk_context_get_units_per_sector(c)) + 1 : (n)) + ++ ++static unsigned long long ++get_abs_partition_start(struct pte *pe) ++{ ++ return pe->offset + get_start_sect(pe->part_table); ++} ++ ++static unsigned long long ++get_abs_partition_end(struct pte *pe) ++{ ++ unsigned long long size; ++ ++ size = get_nr_sects(pe->part_table); ++ return get_abs_partition_start(pe) + size - (size ? 1 : 0); ++} ++ + static void warn_alignment(struct fdisk_context *cxt) + { + if (nowarn) +@@ -1254,67 +1270,113 @@ void dos_list_table_expert(struct fdisk_context *cxt, int extend) + } + } + +-/* +- * Fix the chain of logicals. +- * extended_offset is unchanged, the set of sectors used is unchanged +- * The chain is sorted so that sectors increase, and so that +- * starting sectors increase. +- * +- * After this it may still be that cfdisk doesn't like the table. +- * (This is because cfdisk considers expanded parts, from link to +- * end of partition, and these may still overlap.) +- * Now +- * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda +- * may help. +- */ ++ ++static void print_chain_of_logicals(struct fdisk_context *cxt) ++{ ++ size_t i; ++ ++ fputc('\n', stdout); ++ ++ for (i = 4; i < cxt->label->nparts_max; i++) { ++ struct pte *pe = &ptes[i]; ++ ++ fprintf(stderr, "#%02zu EBR [%10ju], " ++ "data[start=%10ju (%10ju), size=%10ju], " ++ "link[start=%10ju (%10ju), size=%10ju]\n", ++ i, (uintmax_t) pe->offset, ++ /* data */ ++ (uintmax_t) get_start_sect(pe->part_table), ++ (uintmax_t) get_abs_partition_start(pe), ++ (uintmax_t) get_nr_sects(pe->part_table), ++ /* link */ ++ (uintmax_t) get_start_sect(pe->ext_pointer), ++ (uintmax_t) (extended_offset + get_start_sect(pe->ext_pointer)), ++ (uintmax_t) get_nr_sects(pe->ext_pointer)); ++ } ++} ++ ++static int cmp_ebr_offsets(const void *a, const void *b) ++{ ++ struct pte *ae = (struct pte *) a, ++ *be = (struct pte *) b; ++ ++ if (ae->offset == 0 && be->offset == 0) ++ return 0; ++ if (ae->offset == 0) ++ return 1; ++ if (be->offset == 0) ++ return -1; ++ ++ return cmp_numbers(ae->offset, be->offset); ++} ++ + static void fix_chain_of_logicals(struct fdisk_context *cxt) + { +- size_t j, oj, ojj, sj, sjj; +- struct partition *pj,*pjj,tmp; +- +- /* Stage 1: sort sectors but leave sector of part 4 */ +- /* (Its sector is the global extended_offset.) */ +- stage1: +- for (j = 5; j < cxt->label->nparts_max - 1; j++) { +- oj = ptes[j].offset; +- ojj = ptes[j+1].offset; +- if (oj > ojj) { +- ptes[j].offset = ojj; +- ptes[j+1].offset = oj; +- pj = ptes[j].part_table; +- set_start_sect(pj, get_start_sect(pj)+oj-ojj); +- pjj = ptes[j+1].part_table; +- set_start_sect(pjj, get_start_sect(pjj)+ojj-oj); +- set_start_sect(ptes[j-1].ext_pointer, +- ojj-extended_offset); +- set_start_sect(ptes[j].ext_pointer, +- oj-extended_offset); +- goto stage1; ++ struct pte *last; ++ size_t i; ++ ++ DBG(CONTEXT, print_chain_of_logicals(cxt)); ++ ++ /* Sort chain by EBR offsets */ ++ qsort(&ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte), ++ cmp_ebr_offsets); ++ ++again: ++ /* Sort data partitions by start */ ++ for (i = 4; i < cxt->label->nparts_max - 1; i++) { ++ struct pte *cur = &ptes[i], ++ *nxt = &ptes[i + 1]; ++ ++ if (get_abs_partition_start(cur) > ++ get_abs_partition_start(nxt)) { ++ ++ struct partition tmp = *cur->part_table; ++ sector_t cur_start = get_abs_partition_start(cur), ++ nxt_start = get_abs_partition_start(nxt); ++ ++ /* swap data partitions */ ++ *cur->part_table = *nxt->part_table; ++ *nxt->part_table = tmp; ++ ++ /* Recount starts according to EBR offsets, the absolute ++ * address still has to be the same! */ ++ set_start_sect(cur->part_table, nxt_start - cur->offset); ++ set_start_sect(nxt->part_table, cur_start - nxt->offset); ++ ++ cur->changed = 1; ++ nxt->changed = 1; ++ goto again; + } + } + +- /* Stage 2: sort starting sectors */ +- stage2: +- for (j = 4; j < cxt->label->nparts_max - 1; j++) { +- pj = ptes[j].part_table; +- pjj = ptes[j+1].part_table; +- sj = get_start_sect(pj); +- sjj = get_start_sect(pjj); +- oj = ptes[j].offset; +- ojj = ptes[j+1].offset; +- if (oj+sj > ojj+sjj) { +- tmp = *pj; +- *pj = *pjj; +- *pjj = tmp; +- set_start_sect(pj, ojj+sjj-oj); +- set_start_sect(pjj, oj+sj-ojj); +- goto stage2; +- } ++ /* Update EBR links */ ++ for (i = 4; i < cxt->label->nparts_max - 1; i++) { ++ struct pte *cur = &ptes[i], ++ *nxt = &ptes[i + 1]; ++ ++ sector_t noff = nxt->offset - extended_offset, ++ ooff = get_start_sect(cur->ext_pointer); ++ ++ if (noff == ooff) ++ continue; ++ ++ DBG(CONTEXT, dbgprint("DOS: fix EBR [%10ju] link %ju -> %ju", ++ (uintmax_t) cur->offset, ++ (uintmax_t) ooff, (uintmax_t) noff)); ++ ++ set_partition(cxt, i, 1, nxt->offset, ++ get_abs_partition_end(nxt), ++ EXTENDED); ++ } ++ ++ /* always terminate the chain ! */ ++ last = &ptes[cxt->label->nparts_max - 1]; ++ if (last) { ++ clear_partition(last->ext_pointer); ++ last->changed = 1; + } + +- /* Probably something was changed */ +- for (j = 4; j < cxt->label->nparts_max; j++) +- ptes[j].changed = 1; ++ DBG(CONTEXT, print_chain_of_logicals(cxt)); + } + + void dos_fix_partition_table_order(struct fdisk_context *cxt) +-- +2.7.4 + diff --git a/SOURCES/0083-tests-cleanup-tests.patch b/SOURCES/0083-tests-cleanup-tests.patch new file mode 100644 index 0000000..10da5f2 --- /dev/null +++ b/SOURCES/0083-tests-cleanup-tests.patch @@ -0,0 +1,276 @@ +From d35dfbe11f7c6b84d40a6e54611dc3a8c9c27b4d Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 30 Jun 2016 11:00:54 +0200 +Subject: [PATCH 83/86] tests: cleanup tests + +... mostly to be more portable to the new kernels and userspace. + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1153770 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + tests/commands.sh | 1 + + tests/expected/blkid/md-raid0-whole | 2 +- + tests/expected/blkid/md-raid1-part | 2 +- + tests/expected/blkid/md-raid1-whole | 2 +- + tests/expected/fdisk/align-512-4K | 2 +- + tests/expected/fdisk/align-512-4K-63 | 2 +- + tests/expected/fdisk/align-512-4K-md | 4 ++-- + tests/expected/fdisk/align-512-512 | 2 +- + tests/expected/fdisk/align-512-512-topology | 2 +- + tests/expected/ipcs/limits | 4 ---- + tests/functions.sh | 29 +++++++++++++++++++++++------ + tests/ts/ipcs/limits | 5 +---- + tests/ts/ipcs/limits2 | 2 ++ + tests/ts/libmount/context-utab | 10 ++++++++-- + 14 files changed, 44 insertions(+), 25 deletions(-) + +diff --git a/tests/commands.sh b/tests/commands.sh +index a2948c9..e769b5d 100644 +--- a/tests/commands.sh ++++ b/tests/commands.sh +@@ -64,3 +64,4 @@ TS_CMD_UL=${TS_CMD_UL-"$top_builddir/ul"} + TS_CMD_UMOUNT=${TS_CMD_UMOUNT:-"$top_builddir/umount"} + TS_CMD_UTMPDUMP=${TS_CMD_UTMPDUMP-"$top_builddir/utmpdump"} + TS_CMD_WHEREIS=${TS_CMD_WHEREIS-"$top_builddir/whereis"} ++TS_CMD_WIPEFS=${TS_CMD_WIPEFS-"$top_builddir/wipefs"} +diff --git a/tests/expected/blkid/md-raid0-whole b/tests/expected/blkid/md-raid0-whole +index 37dabf3..30825de 100644 +--- a/tests/expected/blkid/md-raid0-whole ++++ b/tests/expected/blkid/md-raid0-whole +@@ -26,7 +26,7 @@ Command (m for help): + Disk /dev/md8: 104 MB, 104726528 bytes, 204544 sectors + Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 512 bytes +-I/O size (minimum/optimal): 65536 bytes / 131072 bytes ++I/O size (minimum/optimal): 65536 bytes / <removed> bytes + Disk label type: dos + + +diff --git a/tests/expected/blkid/md-raid1-part b/tests/expected/blkid/md-raid1-part +index f409952..3d1b38c 100644 +--- a/tests/expected/blkid/md-raid1-part ++++ b/tests/expected/blkid/md-raid1-part +@@ -24,7 +24,7 @@ Command (m for help): + Disk /dev/...: 53 MB, 53477376 bytes, 104448 sectors + Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 512 bytes +-I/O size (minimum/optimal): 512 bytes / 32768 bytes ++I/O size (minimum/optimal): 512 bytes / <removed> bytes + Disk label type: dos + + +diff --git a/tests/expected/blkid/md-raid1-whole b/tests/expected/blkid/md-raid1-whole +index 8977312..8ab1da2 100644 +--- a/tests/expected/blkid/md-raid1-whole ++++ b/tests/expected/blkid/md-raid1-whole +@@ -25,7 +25,7 @@ Command (m for help): + Disk /dev/md8: 52 MB, 52363264 bytes, 102272 sectors + Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 512 bytes +-I/O size (minimum/optimal): 512 bytes / 512 bytes ++I/O size (minimum/optimal): 512 bytes / <removed> bytes + Disk label type: dos + + +diff --git a/tests/expected/fdisk/align-512-4K b/tests/expected/fdisk/align-512-4K +index e5a28f5..7b52fd2 100644 +--- a/tests/expected/fdisk/align-512-4K ++++ b/tests/expected/fdisk/align-512-4K +@@ -57,7 +57,7 @@ Command (m for help): + Disk /dev/...: 52 MB, 52428800 bytes, 102400 sectors + Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 4096 bytes +-I/O size (minimum/optimal): 4096 bytes / 32768 bytes ++I/O size (minimum/optimal): 4096 bytes / <removed> bytes + Disk label type: dos + + +diff --git a/tests/expected/fdisk/align-512-4K-63 b/tests/expected/fdisk/align-512-4K-63 +index 7e236ce..302a429 100644 +--- a/tests/expected/fdisk/align-512-4K-63 ++++ b/tests/expected/fdisk/align-512-4K-63 +@@ -57,7 +57,7 @@ Command (m for help): + Disk /dev/...: 52 MB, 52428800 bytes, 102400 sectors + Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 4096 bytes +-I/O size (minimum/optimal): 4096 bytes / 32768 bytes ++I/O size (minimum/optimal): 4096 bytes / <removed> bytes + Alignment offset: 3584 bytes + Disk label type: dos + +diff --git a/tests/expected/fdisk/align-512-4K-md b/tests/expected/fdisk/align-512-4K-md +index dbbbc05..e8f6ba1 100644 +--- a/tests/expected/fdisk/align-512-4K-md ++++ b/tests/expected/fdisk/align-512-4K-md +@@ -28,7 +28,7 @@ Command (m for help): + Disk /dev/...: 52 MB, 52428800 bytes, 102400 sectors + Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 4096 bytes +-I/O size (minimum/optimal): 4096 bytes / 32768 bytes ++I/O size (minimum/optimal): 4096 bytes / <removed> bytes + Disk label type: dos + + +@@ -72,7 +72,7 @@ Command (m for help): + Disk /dev/md8: 51 MB, 51249152 bytes, 100096 sectors + Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 4096 bytes +-I/O size (minimum/optimal): 65536 bytes / 131072 bytes ++I/O size (minimum/optimal): 65536 bytes / <removed> bytes + Disk label type: dos + + +diff --git a/tests/expected/fdisk/align-512-512 b/tests/expected/fdisk/align-512-512 +index 018322f..8e03d90 100644 +--- a/tests/expected/fdisk/align-512-512 ++++ b/tests/expected/fdisk/align-512-512 +@@ -53,7 +53,7 @@ Command (m for help): + Disk /dev/loop0: 52 MB, 52428800 bytes, 102400 sectors + Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 512 bytes +-I/O size (minimum/optimal): 512 bytes / 512 bytes ++I/O size (minimum/optimal): 512 bytes / <removed> bytes + Disk label type: dos + + +diff --git a/tests/expected/fdisk/align-512-512-topology b/tests/expected/fdisk/align-512-512-topology +index b3d3114..fd57f60 100644 +--- a/tests/expected/fdisk/align-512-512-topology ++++ b/tests/expected/fdisk/align-512-512-topology +@@ -53,7 +53,7 @@ Command (m for help): + Disk /dev/...: 52 MB, 52428800 bytes, 102400 sectors + Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 512 bytes +-I/O size (minimum/optimal): 512 bytes / 32768 bytes ++I/O size (minimum/optimal): 512 bytes / <removed> bytes + Disk label type: dos + + +diff --git a/tests/expected/ipcs/limits b/tests/expected/ipcs/limits +index 7480a1e..e08fa54 100644 +--- a/tests/expected/ipcs/limits ++++ b/tests/expected/ipcs/limits +@@ -1,8 +1,4 @@ + load original values +-check for difference between kernel and IPC +-/proc/sys/kernel/shmmni OK +-/proc/sys/kernel/shmall OK +-/proc/sys/kernel/shmmax OK + maximalize kernel setting + re-check for difference between kernel and IPC + /proc/sys/kernel/shmmni OK +diff --git a/tests/functions.sh b/tests/functions.sh +index b2d493c..0d1c9c8 100644 +--- a/tests/functions.sh ++++ b/tests/functions.sh +@@ -31,6 +31,17 @@ function ts_canonicalize { + fi + } + ++function ts_check_test_command { ++ if [ ! -x "$1" ]; then ++ ts_skip "${1##*/} not found" ++ fi ++} ++ ++function ts_check_prog { ++ local cmd=$1 ++ type "$cmd" >/dev/null 2>&1 || ts_skip "missing in PATH: $cmd" ++} ++ + function ts_skip_subtest { + echo " IGNORE ($1)" + } +@@ -474,14 +485,20 @@ function ts_fdisk_clean { + local DEVNAME=$(basename "$1") + + # remove non comparable parts of fdisk output +- if [ x"${DEVNAME}" != x"" ]; then +- sed -i -e "s/\/dev\/${DEVNAME}/\/dev\/.../g" $TS_OUTPUT ++ if [ -n "${DEVNAME}" ]; then ++ sed -i -e "s@${DEVNAME}@...@;" $TS_OUTPUT + fi + +- sed -i -e 's/Disk identifier:.*//g' \ +- -e 's/Building a new.*//g' \ +- -e 's/Welcome to fdisk.*//g' \ +- $TS_OUTPUT ++ sed -i \ ++ -e 's/Disk identifier:.*//' \ ++ -e 's/Building a new.*//' \ ++ -e 's/Created a new.*//' \ ++ -e 's/^Device[[:blank:]]*Start/Device Start/' \ ++ -e 's/^Device[[:blank:]]*Boot/Device Boot/' \ ++ -e 's/Welcome to fdisk.*//' \ ++ -e 's/typescript file.*/typescript file <removed>./' \ ++ -e 's@^\(I/O size (minimum/op.* bytes /\) [1-9][0-9]* @\1 <removed> @' \ ++ $TS_OUTPUT + } + + function ts_scsi_debug_init { +diff --git a/tests/ts/ipcs/limits b/tests/ts/ipcs/limits +index b44adb1..c4717c3 100755 +--- a/tests/ts/ipcs/limits ++++ b/tests/ts/ipcs/limits +@@ -23,7 +23,7 @@ TS_DESC="limits overflow" + + ts_init "$*" + ts_skip_nonroot +-type bc >/dev/null 2>&1 || ts_skip "cannot find bc command" ++ts_check_prog "bc" + + . $TS_SELF/functions.sh + +@@ -32,9 +32,6 @@ for i in $IPCS_IDX; do + SHM_ORG[$i]=$(cat ${IPCS_PROCFILES[$i]}) + done >> $TS_OUTPUT + +-ts_log "check for difference between kernel and IPC" +-ipcs_limits_check >> $TS_OUTPUT +- + ts_log "maximalize kernel setting" + for i in $IPCS_IDX; do + echo ${IPCS_LIMITS[$i]} >> ${IPCS_PROCFILES[$i]} +diff --git a/tests/ts/ipcs/limits2 b/tests/ts/ipcs/limits2 +index 63f834d..2eabfd2 100755 +--- a/tests/ts/ipcs/limits2 ++++ b/tests/ts/ipcs/limits2 +@@ -22,6 +22,8 @@ TS_DESC="basic limits" + . $TS_TOPDIR/functions.sh + ts_init "$*" + ++ts_check_prog "bc" ++ + . $TS_SELF/functions.sh + + # TODO https://github.com/karelzak/util-linux/issues/51 +diff --git a/tests/ts/libmount/context-utab b/tests/ts/libmount/context-utab +index 859fc2b..b2bfefc 100755 +--- a/tests/ts/libmount/context-utab ++++ b/tests/ts/libmount/context-utab +@@ -87,14 +87,20 @@ ts_finalize_subtest + + + if [ -x "/sbin/mkfs.btrfs" ]; then ++ + $TS_CMD_WIPEFS -a $DEVICE &> /dev/null + ts_log "Create filesystem [btrfs]" + /sbin/mkfs.btrfs -L "$LABEL" $DEVICE &> /dev/null + udevadm settle + + mount -t btrfs $DEVICE $MOUNTPOINT &> /dev/null +- /sbin/btrfsctl -S sub $MOUNTPOINT &> /dev/null +- umount $MOUNTPOINT &> /dev/null ++ ++ if [ -x "/sbin/btrfsctl" ]; then ++ /sbin/btrfsctl -S sub $MOUNTPOINT &> /dev/null ++ else ++ btrfs subvolume create $MOUNTPOINT/sub &> /dev/null ++ fi ++ umount $MOUNTPOINT #&> /dev/null + + udevadm settle + +-- +2.7.4 + diff --git a/SOURCES/0084-libmount-add-support-for-bind-ro.patch b/SOURCES/0084-libmount-add-support-for-bind-ro.patch new file mode 100644 index 0000000..f86edbb --- /dev/null +++ b/SOURCES/0084-libmount-add-support-for-bind-ro.patch @@ -0,0 +1,160 @@ +From 2218dc0d130bb72809e2d8b26a36402bf6293727 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 17 Aug 2015 11:54:26 +0200 +Subject: [PATCH 84/86] libmount: add support for "bind,ro" + +Now it's necessary to use two mount(8) calls to create a read-only +mount: + + mount /foo /bar -o bind + mount /bar -o remount,ro,bind + +This patch allows to specify "bind,ro" and the remount is done +automatically by libmount by additional mount(2) syscall. It's not +atomic of course. + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1281839 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libmount/src/context_mount.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ + sys-utils/mount.8 | 37 ++++++++++++++++++----------------- + 2 files changed, 65 insertions(+), 18 deletions(-) + +diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c +index d6691eb..4df2646 100644 +--- a/libmount/src/context_mount.c ++++ b/libmount/src/context_mount.c +@@ -62,6 +62,10 @@ static int mnt_context_append_additional_mount(struct libmnt_context *cxt, + return 0; + } + ++/* ++ * add additional mount(2) syscall requests when necessary to set propagation flags ++ * after regular mount(2). ++ */ + static int init_propagation(struct libmnt_context *cxt) + { + char *name; +@@ -102,6 +106,41 @@ static int init_propagation(struct libmnt_context *cxt) + } + + /* ++ * add additional mount(2) syscall request to implement "ro,bind", the first regular ++ * mount(2) is the "bind" operation, the second is "remount,ro,bind" call. ++ * ++ * Note that we don't remove "ro" from the first syscall (kernel silently ++ * ignores this flags for bind operation) -- maybe one day kernel will support ++ * read-only binds in one step and then all will be done by the firts mount(2) and the ++ * second remount will be noop... ++ */ ++static int init_robind(struct libmnt_context *cxt) ++{ ++ struct libmnt_addmount *ad; ++ int rc; ++ ++ assert(cxt); ++ assert(cxt->mountflags & MS_BIND); ++ assert(cxt->mountflags & MS_RDONLY); ++ assert(!(cxt->mountflags & MS_REMOUNT)); ++ ++ DBG(CXT, mnt_debug_h(cxt, "mount: initialize additional ro,bind mount")); ++ ++ ad = mnt_new_addmount(); ++ if (!ad) ++ return -ENOMEM; ++ ++ ad->mountflags = MS_REMOUNT | MS_BIND | MS_RDONLY; ++ if (cxt->mountflags & MS_REC) ++ ad->mountflags |= MS_REC; ++ rc = mnt_context_append_additional_mount(cxt, ad); ++ if (rc) ++ return rc; ++ ++ return 0; ++} ++ ++/* + * this has to be called after mnt_context_evaluate_permissions() + */ + static int fix_optstr(struct libmnt_context *cxt) +@@ -174,6 +213,13 @@ static int fix_optstr(struct libmnt_context *cxt) + if (rc) + return rc; + } ++ if ((cxt->mountflags & MS_BIND) ++ && (cxt->mountflags & MS_RDONLY) ++ && !(cxt->mountflags & MS_REMOUNT)) { ++ rc = init_robind(cxt); ++ if (rc) ++ return rc; ++ } + + next = fs->fs_optstr; + +diff --git a/sys-utils/mount.8 b/sys-utils/mount.8 +index 3648870..49cb281 100644 +--- a/sys-utils/mount.8 ++++ b/sys-utils/mount.8 +@@ -388,25 +388,25 @@ or shortoption + .\" available since Linux 2.4.11. + + Note that the filesystem mount options will remain the same as those +-on the original mount point, and cannot be changed by passing the -o +-option along with --bind/--rbind. The mount options can be +-changed by a separate remount command, for example: ++on the original mount point. ++ ++.BR mount(8) ++since v2.27 (backported to RHEL7.3) allow to change the options by passing the ++.B \-o ++option along with ++.BR \-\-bind ++for example: + + .RS + .br +-.B mount --bind +-.I olddir newdir +-.br +-.B mount -o remount,ro +-.I newdir ++.B mount \-\-bind,ro foo foo + .RE + +-Note that behavior of the remount operation depends on the /etc/mtab file. The +-first command stores the 'bind' flag to the /etc/mtab file and the second +-command reads the flag from the file. If you have a system without the +-/etc/mtab file or if you explicitly define source and target for the remount +-command (then mount(8) does not read /etc/mtab), then you have to use bind flag +-(or option) for the remount command too. For example: ++This feature is not supported by Linux kernel and it is implemented in userspace ++by additional remount mount(2) syscall. This solution is not atomic. ++ ++The alternative (classic) way to create a read-only bind mount is to use remount ++operation, for example: + + .RS + .br +@@ -417,14 +417,15 @@ command (then mount(8) does not read /etc/mtab), then you have to use bind flag + .I olddir newdir + .RE + +-Note that +-.I remount,ro,bind +-will create a read-only mountpoint (VFS entry), but the original filesystem suberblock +-will be still writable, it means that the ++Note that read-only bind will create a read-only mountpoint (VFS entry), but the ++original filesystem superblock will still be writable, meaning that the + .I olddir + will be writable, but the + .I newdir + will be read-only. ++ ++It's impossible to change mount options recursively ++(for example with \fB -o rbind,ro\fR). + .RE + + .B The move operation. +-- +2.7.4 + diff --git a/SOURCES/0085-libblkid-store-only-canonical-devnames-to-the-cache.patch b/SOURCES/0085-libblkid-store-only-canonical-devnames-to-the-cache.patch new file mode 100644 index 0000000..0ee3aa3 --- /dev/null +++ b/SOURCES/0085-libblkid-store-only-canonical-devnames-to-the-cache.patch @@ -0,0 +1,176 @@ +From 75b6c0e045abb7e07773b924237c562ab9920c60 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 26 May 2016 12:02:12 +0200 +Subject: [PATCH 85/86] libblkid: store only canonical devnames to the cache + +Let's try to use symlink: + + # ls -la /dev/block/8\:1 + # lrwxrwxrwx 1 root root 7 May 25 16:42 /dev/block/8:1 -> ../sda1 + + # blkid /dev/block/8:1 + /dev/block/8:3: LABEL="HOME" UUID="196972ad-3b13-4bba-ac54-4cb3f7b409a4" TYPE="ext4" PARTUUID="6073277f-87bc-43ff-bcfd-724c4484a63a" + +unfortunately the symlink is stored to the cache: + + <device DEVNO="0x0803" TIME="1464253300.715279" LABEL="HOME" UUID="196972ad-3b13-4bba-ac54-4cb3f7b409a4" TYPE="ext4" PARTUUID="6073277f-87bc-43ff-bcfd-724c4484a63a">/dev/block/8:3</device> + +next time if you ask for LABEL=HOME the answer will be /dev/block/8:3 +rather than /dev/sda3. + +It seems better to canonicalize the paths we store to the cache. + +Unfortunately if you ask for /dev/block/8:3 then you probably expect +that blkid_dev_devname() returns the same path. This patch introduces +dev->bid_xname, this is the path used by application (and never stored +in the cache). + +Upstream: http://github.com/karelzak/util-linux/commit/924c93d9df118338fd54cd73b4a45ccddc4ac103 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1007734 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/blkidP.h | 4 +++- + libblkid/src/dev.c | 11 +++++++++-- + libblkid/src/devname.c | 43 +++++++++++++++++++++++++++++++++++++------ + 3 files changed, 49 insertions(+), 9 deletions(-) + +diff --git a/libblkid/src/blkidP.h b/libblkid/src/blkidP.h +index 7de84b4..8183c12 100644 +--- a/libblkid/src/blkidP.h ++++ b/libblkid/src/blkidP.h +@@ -44,7 +44,7 @@ struct blkid_struct_dev + struct list_head bid_devs; /* All devices in the cache */ + struct list_head bid_tags; /* All tags for this device */ + blkid_cache bid_cache; /* Dev belongs to this cache */ +- char *bid_name; /* Device inode pathname */ ++ char *bid_name; /* Device real pathn (as used in cache) */ + char *bid_type; /* Preferred device TYPE */ + int bid_pri; /* Device priority */ + dev_t bid_devno; /* Device major/minor number */ +@@ -53,6 +53,8 @@ struct blkid_struct_dev + unsigned int bid_flags; /* Device status bitflags */ + char *bid_label; /* Shortcut to device LABEL */ + char *bid_uuid; /* Shortcut to binary UUID */ ++ ++ char *bid_xname; /* Device path as used by application (maybe symlink..) */ + }; + + #define BLKID_BID_FL_VERIFIED 0x0001 /* Device data validated from disk */ +diff --git a/libblkid/src/dev.c b/libblkid/src/dev.c +index a4b2aea..d2fd3f4 100644 +--- a/libblkid/src/dev.c ++++ b/libblkid/src/dev.c +@@ -60,16 +60,23 @@ void blkid_free_dev(blkid_dev dev) + bit_tags); + blkid_free_tag(tag); + } ++ free(dev->bid_xname); + free(dev->bid_name); + free(dev); + } + + /* +- * Given a blkid device, return its name ++ * Given a blkid device, return its name. The function returns the name ++ * previously used for blkid_get_dev(). This name does not have to be canonical ++ * (real path) name, but for example symlink. + */ + const char *blkid_dev_devname(blkid_dev dev) + { +- return dev ? dev->bid_name : NULL; ++ if (!dev) ++ return NULL; ++ if (dev->bid_xname) ++ return dev->bid_xname; ++ return dev->bid_name; + } + + #ifdef CONFIG_BLKID_DEBUG +diff --git a/libblkid/src/devname.c b/libblkid/src/devname.c +index 497deaf..55b9594 100644 +--- a/libblkid/src/devname.c ++++ b/libblkid/src/devname.c +@@ -51,28 +51,55 @@ blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags) + { + blkid_dev dev = NULL, tmp; + struct list_head *p, *pnext; ++ char *cn = NULL; + + if (!cache || !devname) + return NULL; + ++ /* search by name */ + list_for_each(p, &cache->bic_devs) { + tmp = list_entry(p, struct blkid_struct_dev, bid_devs); + if (strcmp(tmp->bid_name, devname)) + continue; +- +- DBG(DEVNAME, blkid_debug("found devname %s in cache", tmp->bid_name)); + dev = tmp; + break; + } + ++ /* try canonicalize the name */ ++ if (!dev && (cn = canonicalize_path(devname))) { ++ if (strcmp(cn, devname) != 0) { ++ DBG(DEVNAME, blkid_debug("search cannonical %s", cn)); ++ list_for_each(p, &cache->bic_devs) { ++ tmp = list_entry(p, struct blkid_struct_dev, bid_devs); ++ if (strcmp(tmp->bid_name, cn)) ++ continue; ++ dev = tmp; ++ ++ /* update name returned by blkid_dev_devname() */ ++ free(dev->bid_xname); ++ dev->bid_xname = strdup(devname); ++ break; ++ } ++ } else { ++ free(cn); ++ cn = NULL; ++ } ++ } ++ + if (!dev && (flags & BLKID_DEV_CREATE)) { + if (access(devname, F_OK) < 0) +- return NULL; ++ goto done; + dev = blkid_new_dev(); + if (!dev) +- return NULL; ++ goto done; + dev->bid_time = INT_MIN; +- dev->bid_name = strdup(devname); ++ if (cn) { ++ dev->bid_name = cn; ++ dev->bid_xname = strdup(devname); ++ cn = NULL; /* see free() below */ ++ } else ++ dev->bid_name = strdup(devname); ++ + dev->bid_cache = cache; + list_add_tail(&dev->bid_devs, &cache->bic_devs); + cache->bic_flags |= BLKID_BIC_FL_CHANGED; +@@ -81,7 +108,7 @@ blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags) + if (flags & BLKID_DEV_VERIFY) { + dev = blkid_verify(cache, dev); + if (!dev || !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) +- return dev; ++ goto done; + /* + * If the device is verified, then search the blkid + * cache for any entries that match on the type, uuid, +@@ -112,6 +139,10 @@ blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags) + blkid_free_dev(dev2); + } + } ++done: ++ if (dev) ++ DBG(DEVNAME, blkid_debug("%s requested, found %s in cache", devname, dev->bid_name)); ++ free(cn); + return dev; + } + +-- +2.7.4 + diff --git a/SOURCES/0086-libblkid-avoid-recursion-in-EBR.patch b/SOURCES/0086-libblkid-avoid-recursion-in-EBR.patch new file mode 100644 index 0000000..32b7f17 --- /dev/null +++ b/SOURCES/0086-libblkid-avoid-recursion-in-EBR.patch @@ -0,0 +1,102 @@ +From 8125a64ff9e98d09c659dbd5adbca521d63a268b Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 7 Jul 2016 14:22:41 +0200 +Subject: [PATCH 86/86] libblkid: avoid recursion in EBR + +Upstream: http://github.com/karelzak/util-linux/commit/7164a1c34d18831ac61c6744ad14ce916d389b3f +Upstream: http://github.com/karelzak/util-linux/commit/50d1594c2e6142a3b51d2143c74027480df082e0 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1349536 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/partitions/dos.c | 21 +++++++++++++++++++-- + libblkid/src/partitions/partitions.c | 14 ++++++++++++++ + libblkid/src/partitions/partitions.h | 2 ++ + 3 files changed, 35 insertions(+), 2 deletions(-) + +diff --git a/libblkid/src/partitions/dos.c b/libblkid/src/partitions/dos.c +index 2d4a537..563fe9a 100644 +--- a/libblkid/src/partitions/dos.c ++++ b/libblkid/src/partitions/dos.c +@@ -48,6 +48,12 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab, + int ct_nodata = 0; /* count ext.partitions without data partitions */ + int i; + ++ DBG(LOWPROBE, blkid_debug("parse EBR [start=%d, size=%d]", ex_start/ssf, ex_size/ssf)); ++ if (ex_start == 0) { ++ DBG(LOWPROBE, blkid_debug("Bad offset in primary extended partition -- ignore")); ++ return 0; ++ } ++ + while (1) { + struct dos_partition *p, *p0; + uint32_t start, size; +@@ -100,6 +106,13 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab, + continue; + } + ++ /* Avoid recursive non-empty links, see ct_nodata counter */ ++ if (blkid_partlist_get_partition_by_start(ls, abs_start)) { ++ DBG(LOWPROBE, blkid_debug("#%d: EBR duplicate data partition [abs start=%u] -- ignore", ++ i + 1, abs_start)); ++ continue; ++ } ++ + par = blkid_partlist_add_partition(ls, tab, abs_start, size); + if (!par) + return -ENOMEM; +@@ -116,8 +129,12 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab, + start = dos_partition_start(p) * ssf; + size = dos_partition_size(p) * ssf; + +- if (size && is_extended(p)) +- break; ++ if (size && is_extended(p)) { ++ if (start == 0) ++ DBG(LOWPROBE, blkid_debug("#%d: EBR link offset is zero -- ignore", i + 1)); ++ else ++ break; ++ } + } + if (i == 4) + goto leave; +diff --git a/libblkid/src/partitions/partitions.c b/libblkid/src/partitions/partitions.c +index 752fc95..9d846ff 100644 +--- a/libblkid/src/partitions/partitions.c ++++ b/libblkid/src/partitions/partitions.c +@@ -928,6 +928,20 @@ blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n) + return &ls->parts[n]; + } + ++blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start) ++{ ++ int i, nparts; ++ blkid_partition par; ++ ++ nparts = blkid_partlist_numof_partitions(ls); ++ for (i = 0; i < nparts; i++) { ++ par = blkid_partlist_get_partition(ls, i); ++ if ((uint64_t) blkid_partition_get_start(par) == start) ++ return par; ++ } ++ return NULL; ++} ++ + /** + * blkid_partlist_devno_to_partition: + * @ls: partitions list +diff --git a/libblkid/src/partitions/partitions.h b/libblkid/src/partitions/partitions.h +index 61763bf..4e99e2a 100644 +--- a/libblkid/src/partitions/partitions.h ++++ b/libblkid/src/partitions/partitions.h +@@ -20,6 +20,8 @@ extern int blkid_partlist_increment_partno(blkid_partlist ls); + + extern blkid_partition blkid_partlist_get_parent(blkid_partlist ls); + ++extern blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start); ++ + extern int blkid_partitions_do_subprobe(blkid_probe pr, + blkid_partition parent, const struct blkid_idinfo *id); + +-- +2.7.4 + diff --git a/SOURCES/0087-findmnt-fix-target-behaviour.patch b/SOURCES/0087-findmnt-fix-target-behaviour.patch new file mode 100644 index 0000000..289e599 --- /dev/null +++ b/SOURCES/0087-findmnt-fix-target-behaviour.patch @@ -0,0 +1,106 @@ +From fbdd962ca187399cab3b0bf78d740e5fb304e0bf Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 16 Dec 2016 12:19:32 +0100 +Subject: [PATCH] findmnt: fix --target behaviour + +Upstream: https://github.com/karelzak/util-linux/commit/80c31a0b596125b387c6b27c899e8bad4e46680b +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1405238 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libmount/src/mountP.h | 1 + + libmount/src/tab.c | 4 ++++ + libmount/src/utils.c | 9 +++++++++ + misc-utils/findmnt.c | 10 +++------- + 4 files changed, 17 insertions(+), 7 deletions(-) + +diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h +index 7b0848f..6cabced 100644 +--- a/libmount/src/mountP.h ++++ b/libmount/src/mountP.h +@@ -164,6 +164,7 @@ extern int mnt_get_filesystems(char ***filesystems, const char *pattern); + extern void mnt_free_filesystems(char **filesystems); + + extern char *mnt_get_kernel_cmdline_option(const char *name); ++extern int mnt_stat_mountpoint(const char *target, struct stat *st); + + /* tab.c */ + extern int mnt_table_set_parser_fltrcb( struct libmnt_table *tb, +diff --git a/libmount/src/tab.c b/libmount/src/tab.c +index 10ee7ce..5628da6 100644 +--- a/libmount/src/tab.c ++++ b/libmount/src/tab.c +@@ -528,6 +528,7 @@ struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb, + int direction) + { + char *mnt; ++ struct stat st; + + if (!tb || !path || !*path) + return NULL; +@@ -536,6 +537,9 @@ struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb, + + DBG(TAB, mnt_debug_h(tb, "lookup MOUNTPOINT: '%s'", path)); + ++ if (mnt_stat_mountpoint(path, &st)) ++ return NULL; ++ + mnt = strdup(path); + if (!mnt) + return NULL; +diff --git a/libmount/src/utils.c b/libmount/src/utils.c +index c13fb96..5c374b4 100644 +--- a/libmount/src/utils.c ++++ b/libmount/src/utils.c +@@ -104,6 +104,15 @@ static int fstype_cmp(const void *v1, const void *v2) + return strcmp(s1, s2); + } + ++int mnt_stat_mountpoint(const char *target, struct stat *st) ++{ ++#ifdef AT_NO_AUTOMOUNT ++ return fstatat(-1, target, st, AT_NO_AUTOMOUNT); ++#else ++ return stat(target, st); ++#endif ++} ++ + /* returns basename and keeps dirname in the @path, if @path is "/" (root) + * then returns empty string */ + char *stripoff_last_component(char *path) +diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c +index fe899db..79ef12e 100644 +--- a/misc-utils/findmnt.c ++++ b/misc-utils/findmnt.c +@@ -239,16 +239,15 @@ static void set_source_match(const char *data) + /* @tb has to be from kernel (so no fstab or so)! */ + static void enable_extra_target_match(struct libmnt_table *tb) + { +- char *cn = NULL; +- const char *tgt = NULL, *mnt = NULL; ++ const char *tgt, *mnt = NULL; + struct libmnt_fs *fs; + + /* + * Check if match pattern is mountpoint, if not use the + * real mountpoint. + */ +- cn = mnt_resolve_path(get_match(COL_TARGET), cache); +- if (!cn) ++ tgt = mnt_resolve_path(get_match(COL_TARGET), cache); ++ if (!tgt) + return; + + fs = mnt_table_find_mountpoint(tb, tgt, MNT_ITER_BACKWARD); +@@ -256,9 +255,6 @@ static void enable_extra_target_match(struct libmnt_table *tb) + mnt = mnt_fs_get_target(fs); + if (mnt && strcmp(mnt, tgt) != 0) + set_match(COL_TARGET, xstrdup(mnt)); /* replace the current setting */ +- +- /* replace the current setting with the real mountpoint */ +- set_match(COL_TARGET, mnt); + } + + +-- +2.7.4 + diff --git a/SOURCES/0088-su-properly-clear-child-PID.patch b/SOURCES/0088-su-properly-clear-child-PID.patch new file mode 100644 index 0000000..ae125a7 --- /dev/null +++ b/SOURCES/0088-su-properly-clear-child-PID.patch @@ -0,0 +1,64 @@ +From 756f8301d5f7959312bea3fa8865bb543d43fd50 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 8 Feb 2017 10:19:56 +0100 +Subject: [PATCH] su: properly clear child PID + +Upstream: http://github.com/karelzak/util-linux/commit/dffab154d29a288aa171ff50263ecc8f2e14a891 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1419474 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + login-utils/su-common.c | 25 +++++++++++++++---------- + 1 file changed, 15 insertions(+), 10 deletions(-) + +diff --git a/login-utils/su-common.c b/login-utils/su-common.c +index d53d690..9b3cad5 100644 +--- a/login-utils/su-common.c ++++ b/login-utils/su-common.c +@@ -357,21 +357,26 @@ create_watching_parent (void) + break; + } + if (pid != (pid_t)-1) +- if (WIFSIGNALED (status)) +- { +- fprintf (stderr, "%s%s\n", strsignal (WTERMSIG (status)), +- WCOREDUMP (status) ? _(" (core dumped)") : ""); +- status = WTERMSIG (status) + 128; +- } +- else +- status = WEXITSTATUS (status); ++ { ++ if (WIFSIGNALED (status)) ++ { ++ fprintf (stderr, "%s%s\n", strsignal (WTERMSIG (status)), ++ WCOREDUMP (status) ? _(" (core dumped)") : ""); ++ status = WTERMSIG (status) + 128; ++ } ++ else ++ status = WEXITSTATUS (status); ++ ++ /* child is gone, don't use the PID anymore */ ++ child = (pid_t) -1; ++ } + else + status = 1; + } + else + status = 1; + +- if (caught_signal) ++ if (caught_signal && child != (pid_t)-1) + { + fprintf (stderr, _("\nSession terminated, killing shell...")); + kill (child, SIGTERM); +@@ -379,7 +384,7 @@ create_watching_parent (void) + + cleanup_pam (PAM_SUCCESS); + +- if (caught_signal) ++ if (caught_signal && (child != (pid_t)-1)) + { + sleep (2); + kill (child, SIGKILL); +-- +2.7.4 + diff --git a/SOURCES/0089-fdisk-fix-Blocks-column-calculation.patch b/SOURCES/0089-fdisk-fix-Blocks-column-calculation.patch new file mode 100644 index 0000000..5e9aec0 --- /dev/null +++ b/SOURCES/0089-fdisk-fix-Blocks-column-calculation.patch @@ -0,0 +1,27 @@ +From acf8de63d2a797850935feeaf6bac2dd21c9b496 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 15 Mar 2017 13:23:56 +0100 +Subject: [PATCH 089/116] fdisk: fix Blocks column calculation + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1344102 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + fdisks/fdiskdoslabel.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fdisks/fdiskdoslabel.c b/fdisks/fdiskdoslabel.c +index b7eb35a..6375692 100644 +--- a/fdisks/fdiskdoslabel.c ++++ b/fdisks/fdiskdoslabel.c +@@ -1202,7 +1202,7 @@ int dos_list_table(struct fdisk_context *cxt, + p = pe->part_table; + if (p && !is_cleared_partition(p)) { + unsigned int psects = get_nr_sects(p); +- unsigned int pblocks = psects; ++ unsigned long pblocks = psects; + unsigned int podd = 0; + struct fdisk_parttype *type = + fdisk_get_parttype_from_code(cxt, p->sys_ind); +-- +2.9.3 + diff --git a/SOURCES/0090-fdisk-fix-menu-for-GPT.patch b/SOURCES/0090-fdisk-fix-menu-for-GPT.patch new file mode 100644 index 0000000..efd40e2 --- /dev/null +++ b/SOURCES/0090-fdisk-fix-menu-for-GPT.patch @@ -0,0 +1,45 @@ +From 64107c0347dde0b7207b31132837864bd5f4c714 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 15 Mar 2017 14:16:25 +0100 +Subject: [PATCH 090/116] fdisk: fix menu for GPT + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1344720 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + fdisks/fdisk.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/fdisks/fdisk.c b/fdisks/fdisk.c +index 2656711..0ec9909 100644 +--- a/fdisks/fdisk.c ++++ b/fdisks/fdisk.c +@@ -87,18 +87,21 @@ static const struct menulist_descr menulist[] = { + {'n', N_("add a new partition"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF | FDISK_DISKLABEL_GPT, 0}}, + {'o', N_("change rotation speed (rpm)"), {0, FDISK_DISKLABEL_SUN}}, + {'o', N_("create a new empty DOS partition table"), {~FDISK_DISKLABEL_OSF, 0}}, +- {'p', N_("print the partition table"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN}}, ++ {'p', N_("print the partition table"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF | FDISK_DISKLABEL_GPT, ++ FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_GPT }}, + {'q', N_("quit without saving changes"), {FDISK_DISKLABEL_ANY, FDISK_DISKLABEL_ANY}}, +- {'r', N_("return to main menu"), {FDISK_DISKLABEL_OSF, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF}}, ++ {'r', N_("return to main menu"), {FDISK_DISKLABEL_OSF, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF | FDISK_DISKLABEL_GPT}}, + {'s', N_("change number of sectors/track"), {0, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN}}, + {'s', N_("create a new empty Sun disklabel"), {~FDISK_DISKLABEL_OSF, 0}}, + {'s', N_("show complete disklabel"), {FDISK_DISKLABEL_OSF, 0}}, + {'t', N_("change a partition's system id"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF, 0}}, + {'u', N_("change display/entry units"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF, 0}}, +- {'v', N_("verify the partition table"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI}}, ++ {'v', N_("verify the partition table"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_GPT, ++ FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_GPT}}, + {'w', N_("write disklabel to disk"), {FDISK_DISKLABEL_OSF, 0}}, +- {'w', N_("write table to disk and exit"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_GPT, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI}}, +- {'x', N_("extra functionality (experts only)"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI, 0}}, ++ {'w', N_("write table to disk and exit"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_GPT, ++ FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_GPT}}, ++ {'x', N_("extra functionality (experts only)"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_GPT, 0}}, + #if !defined (__alpha__) + {'x', N_("link BSD partition to non-BSD partition"), {FDISK_DISKLABEL_OSF, 0}}, + #endif +-- +2.9.3 + diff --git a/SOURCES/0091-logger-backport-size.patch b/SOURCES/0091-logger-backport-size.patch new file mode 100644 index 0000000..41d6ec5 --- /dev/null +++ b/SOURCES/0091-logger-backport-size.patch @@ -0,0 +1,165 @@ +From 1e0289af99737049de97b8cfe342b56d380560bf Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 16 Mar 2017 12:20:58 +0100 +Subject: [PATCH 091/116] logger: backport --size + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1323916 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + misc-utils/logger.1 | 6 ++++++ + misc-utils/logger.c | 34 ++++++++++++++++++++++++---------- + 2 files changed, 30 insertions(+), 10 deletions(-) + +diff --git a/misc-utils/logger.1 b/misc-utils/logger.1 +index 8c4faca..57ca0d5 100644 +--- a/misc-utils/logger.1 ++++ b/misc-utils/logger.1 +@@ -98,6 +98,12 @@ logs the message as informational in the local3 facility. + The default is + .IR user.notice . + .TP ++\fB\-S\fR, \fB\-\-size\fR \fIsize\fR ++Sets the maximum permitted message size. The default is 1KiB, which is ++the limit traditionally used and specified in RFC 3164. When selecting a ++maximum message size, it is important to ensure that the receiver supports ++the max size as well, otherwise messages may become truncated. ++.TP + \fB\-s\fR, \fB\-\-stderr\fR + Output the message to standard error as well as to the system log. + .TP +diff --git a/misc-utils/logger.c b/misc-utils/logger.c +index a331869..dfda018 100644 +--- a/misc-utils/logger.c ++++ b/misc-utils/logger.c +@@ -54,6 +54,8 @@ + #include "closestream.h" + #include "nls.h" + #include "strutils.h" ++#include "xalloc.h" ++#include "all-io.h" + + #define SYSLOG_NAMES + #include <syslog.h> +@@ -183,7 +185,7 @@ inet_socket(const char *servername, const char *port, const int socket_type) + + static void + mysyslog(int fd, int logflags, int pri, char *tag, char *msg) { +- char buf[1000], pid[30], *cp, *tp; ++ char *buf, pid[30], *cp, *tp; + time_t now; + + if (fd > -1) { +@@ -201,11 +203,11 @@ mysyslog(int fd, int logflags, int pri, char *tag, char *msg) { + (void)time(&now); + tp = ctime(&now)+4; + +- snprintf(buf, sizeof(buf), "<%d>%.15s %.200s%s: %.400s", ++ xasprintf(&buf, "<%d>%.15s %.200s%s: %s", + pri, tp, cp, pid, msg); + +- if (write(fd, buf, strlen(buf)+1) < 0) +- return; /* error */ ++ write_all(fd, buf, strlen(buf)+1); ++ free(buf); + } + } + +@@ -221,6 +223,7 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out) + " -i, --id log the process ID too\n" + " -f, --file <file> log the contents of this file\n" + " -h, --help display this help text and exit\n"), out); ++ fputs(_(" -S, --size <num> maximum size for a single message (default 1024)\n"), out); + fputs(_(" -n, --server <name> write to this remote syslog server\n" + " -P, --port <port> use this port for UDP or TCP connection\n" + " -p, --priority <prio> mark given message with this priority\n" +@@ -241,11 +244,12 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out) + int + main(int argc, char **argv) { + int ch, logflags, pri; +- char *tag, buf[1024]; ++ char *tag, *buf; + char *usock = NULL; + char *server = NULL; + char *port = NULL; + int LogSock = -1, socket_type = ALL_TYPES; ++ size_t max_message_size = 1024; + + static const struct option longopts[] = { + { "id", no_argument, 0, 'i' }, +@@ -253,6 +257,7 @@ main(int argc, char **argv) { + { "file", required_argument, 0, 'f' }, + { "priority", required_argument, 0, 'p' }, + { "tag", required_argument, 0, 't' }, ++ { "size", required_argument, 0, 'S' }, + { "socket", required_argument, 0, 'u' }, + { "udp", no_argument, 0, 'd' }, + { "tcp", no_argument, 0, 'T' }, +@@ -271,7 +276,7 @@ main(int argc, char **argv) { + tag = NULL; + pri = LOG_NOTICE; + logflags = 0; +- while ((ch = getopt_long(argc, argv, "f:ip:st:u:dTn:P:Vh", ++ while ((ch = getopt_long(argc, argv, "f:ip:st:u:dTn:P:S:Vh", + longopts, NULL)) != -1) { + switch((char)ch) { + case 'f': /* file to log */ +@@ -297,6 +302,10 @@ main(int argc, char **argv) { + case 'd': + socket_type = TYPE_UDP; + break; ++ case 'S': ++ max_message_size = strtosize_or_err(optarg, ++ _("failed to parse message size")); ++ break; + case 'T': + socket_type = TYPE_TCP; + break; +@@ -327,21 +336,23 @@ main(int argc, char **argv) { + else + openlog(tag ? tag : getlogin(), logflags, 0); + ++ buf = xcalloc(1, max_message_size); ++ + /* log input line if appropriate */ + if (argc > 0) { + register char *p, *endp; + size_t len; + +- for (p = buf, endp = buf + sizeof(buf) - 2; *argv;) { ++ for (p = buf, endp = buf + max_message_size - 2; *argv;) { + len = strlen(*argv); + if (p + len > endp && p > buf) { + if (!usock && !server) + syslog(pri, "%s", buf); + else + mysyslog(LogSock, logflags, pri, tag, buf); +- p = buf; ++ p = buf; + } +- if (len > sizeof(buf) - 1) { ++ if (len > max_message_size - 1) { + if (!usock && !server) + syslog(pri, "%s", *argv++); + else +@@ -360,7 +371,7 @@ main(int argc, char **argv) { + mysyslog(LogSock, logflags, pri, tag, buf); + } + } else { +- while (fgets(buf, sizeof(buf), stdin) != NULL) { ++ while (fgets(buf, max_message_size, stdin) != NULL) { + /* glibc is buggy and adds an additional newline, + so we have to remove it here until glibc is fixed */ + int len = strlen(buf); +@@ -374,6 +385,9 @@ main(int argc, char **argv) { + mysyslog(LogSock, logflags, pri, tag, buf); + } + } ++ ++ free(buf); ++ + if (!usock && !server) + closelog(); + else +-- +2.9.3 + diff --git a/SOURCES/0092-fdisk-print-header-UUID-for-GPT.patch b/SOURCES/0092-fdisk-print-header-UUID-for-GPT.patch new file mode 100644 index 0000000..d7f91ed --- /dev/null +++ b/SOURCES/0092-fdisk-print-header-UUID-for-GPT.patch @@ -0,0 +1,70 @@ +From 6f768ec049944ab39c79fcaa7bdf9622385b7672 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 16 Mar 2017 13:29:50 +0100 +Subject: [PATCH 092/116] fdisk: print header UUID for GPT + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1344726 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + fdisks/fdisk.c | 2 ++ + libfdisk/src/fdiskP.h | 1 + + libfdisk/src/gpt.c | 18 ++++++++++++++++++ + 3 files changed, 21 insertions(+) + +diff --git a/fdisks/fdisk.c b/fdisks/fdisk.c +index 0ec9909..974d735 100644 +--- a/fdisks/fdisk.c ++++ b/fdisks/fdisk.c +@@ -657,6 +657,8 @@ list_disk_geometry(struct fdisk_context *cxt) { + printf(_("Disk label type: %s\n"), cxt->label->name); + if (fdisk_is_disklabel(cxt, DOS)) + dos_print_mbr_id(cxt); ++ if (fdisk_is_disklabel(cxt, GPT)) ++ gpt_print_header_id(cxt); + printf("\n"); + } + +diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h +index ce42860..6711ab8 100644 +--- a/libfdisk/src/fdiskP.h ++++ b/libfdisk/src/fdiskP.h +@@ -315,6 +315,7 @@ extern void fdisk_deinit_label(struct fdisk_label *lb); + + /* gpt.c -- temporary bypass library API... */ + extern void gpt_list_table(struct fdisk_context *cxt, int xtra); ++extern void gpt_print_header_id(struct fdisk_context *cxt); + + /* ask.c */ + extern int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew); +diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c +index d3bdc2d..899e1b2 100644 +--- a/libfdisk/src/gpt.c ++++ b/libfdisk/src/gpt.c +@@ -341,6 +341,24 @@ static inline int partition_unused(const struct gpt_entry *e) + sizeof(struct gpt_guid)); + } + ++void gpt_print_header_id(struct fdisk_context *cxt) ++{ ++ char str[37]; ++ struct gpt_header *header; ++ struct fdisk_gpt_label *gpt; ++ ++ assert(cxt); ++ assert(cxt->label); ++ assert(fdisk_is_disklabel(cxt, GPT)); ++ ++ gpt = self_label(cxt); ++ header = gpt->pheader ? gpt->pheader : gpt->bheader; ++ ++ guid_to_string(&header->disk_guid, str); ++ ++ printf("Disk identifier: %s\n", str); ++} ++ + /* + * Builds a clean new valid protective MBR - will wipe out any existing data. + * Returns 0 on success, otherwise < 0 on error. +-- +2.9.3 + diff --git a/SOURCES/0093-fdisk-improve-l-error-handling.patch b/SOURCES/0093-fdisk-improve-l-error-handling.patch new file mode 100644 index 0000000..8471c1a --- /dev/null +++ b/SOURCES/0093-fdisk-improve-l-error-handling.patch @@ -0,0 +1,76 @@ +From e4f26d51bd06910634c2aaeccfecc7e6cd61ef0a Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 17 Mar 2017 11:47:00 +0100 +Subject: [PATCH 093/116] fdisk: improve -l error handling + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1362662 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + fdisks/fdisk.c | 23 ++++++++++++++++------- + 1 file changed, 16 insertions(+), 7 deletions(-) + +diff --git a/fdisks/fdisk.c b/fdisks/fdisk.c +index 974d735..177921a 100644 +--- a/fdisks/fdisk.c ++++ b/fdisks/fdisk.c +@@ -924,11 +924,15 @@ static int is_ide_cdrom_or_tape(char *device) + } + + /* Print disk geometry and partition table of a specified device (-l option) */ +-static void print_partition_table_from_option(struct fdisk_context *cxt, +- char *device, unsigned long sector_size) ++static int print_partition_table_from_option(struct fdisk_context *cxt, ++ char *device, unsigned long sector_size, ++ int warnme) + { +- if (fdisk_context_assign_device(cxt, device, 1) != 0) /* read-only */ +- err(EXIT_FAILURE, _("cannot open %s"), device); ++ if (fdisk_context_assign_device(cxt, device, 1) != 0) { /* read-only */ ++ if (warnme || errno == EACCES) ++ warn(_("cannot open %s"), device); ++ return -1; ++ } + + if (sector_size) /* passed -b option, override autodiscovery */ + fdisk_override_sector_size(cxt, sector_size); +@@ -941,6 +945,8 @@ static void print_partition_table_from_option(struct fdisk_context *cxt, + list_table(cxt, 0); + else + list_disk_geometry(cxt); ++ ++ return 0; + } + + /* +@@ -971,7 +977,7 @@ print_all_partition_table_from_option(struct fdisk_context *cxt, + char *cn = canonicalize_path(devname); + if (cn) { + if (!is_ide_cdrom_or_tape(cn)) +- print_partition_table_from_option(cxt, cn, sector_size); ++ print_partition_table_from_option(cxt, cn, sector_size, 0); + free(cn); + } + } +@@ -1223,14 +1229,17 @@ int main(int argc, char **argv) + " be used with one specified device\n")); + + if (optl) { ++ int rc = 0; ++ + nowarn = 1; + if (argc > optind) { + int k; + for (k = optind; k < argc; k++) +- print_partition_table_from_option(cxt, argv[k], sector_size); ++ rc += print_partition_table_from_option(cxt, argv[k], sector_size, 1); + } else + print_all_partition_table_from_option(cxt, sector_size); +- exit(EXIT_SUCCESS); ++ ++ exit(rc ? EXIT_FAILURE : EXIT_SUCCESS); + } + + if (opts) { +-- +2.9.3 + diff --git a/SOURCES/0094-losetup-improve-man-page-SYNOPSIS.patch b/SOURCES/0094-losetup-improve-man-page-SYNOPSIS.patch new file mode 100644 index 0000000..df5427a --- /dev/null +++ b/SOURCES/0094-losetup-improve-man-page-SYNOPSIS.patch @@ -0,0 +1,42 @@ +From 38899e30a9a195adb4732185ed62d55a03ecc96d Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 17 Mar 2017 14:02:39 +0100 +Subject: [PATCH 094/116] losetup: improve man page SYNOPSIS + +and remove obsolete "-p fd" from the man page. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1369436 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/losetup.8 | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/sys-utils/losetup.8 b/sys-utils/losetup.8 +index bd73518..9a8c1d5 100644 +--- a/sys-utils/losetup.8 ++++ b/sys-utils/losetup.8 +@@ -37,7 +37,7 @@ Print name of first unused loop device: + .B "losetup \-f" + .sp + .in -5 +-Setup loop device: ++Set up a loop device: + .sp + .in +5 + .B losetup +@@ -46,10 +46,8 @@ Setup loop device: + .RB [ \-\-sizelimit + .IR size ] + .in +8 +-.RB [ \-p +-.IR pfd ] +-.RB [ \-rP ] +-.RB { \-f [ \-\-show ]| \fIloopdev\fP } ++.RB [ \-Pr ] ++.RB [ \-\-show ] " \-f" | \fIloopdev\fP + .I file + .sp + .in -13 +-- +2.9.3 + diff --git a/SOURCES/0095-libblkid-fix-potential-bufer-overflows.patch b/SOURCES/0095-libblkid-fix-potential-bufer-overflows.patch new file mode 100644 index 0000000..5eceb9a --- /dev/null +++ b/SOURCES/0095-libblkid-fix-potential-bufer-overflows.patch @@ -0,0 +1,103 @@ +From 55540ea3dfdc707dc998333fd0715549522464fb Mon Sep 17 00:00:00 2001 +From: Sebastian Krahmer <krahmer@suse.de> +Date: Fri, 5 Dec 2014 10:06:42 +0100 +Subject: [PATCH 095/116] libblkid: fix potential bufer overflows + +While digging deeper into libblk probing, I found that some +computations might wrap and allocate too few buffer space which then +overflows. In particular on 32bit systems (chromebook) where size_t is +32bit, this is problematic (for 64bit the result fits into the calloc +size_t). + +Upstream: Upstream: https://github.com/karelzak/util-linux/commit/109df14fad4e9570e26950913ebace6c79289400 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1392656 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/partitions/gpt.c | 12 ++++++++---- + libblkid/src/probe.c | 7 +++++++ + libblkid/src/superblocks/zfs.c | 3 +++ + 3 files changed, 18 insertions(+), 4 deletions(-) + +diff --git a/libblkid/src/partitions/gpt.c b/libblkid/src/partitions/gpt.c +index 6ab0dc6..e801ea3 100644 +--- a/libblkid/src/partitions/gpt.c ++++ b/libblkid/src/partitions/gpt.c +@@ -17,6 +17,7 @@ + #include <stdlib.h> + #include <stdint.h> + #include <stddef.h> ++#include <limits.h> + + #include "partitions.h" + #include "crc32.h" +@@ -266,14 +267,17 @@ static struct gpt_header *get_gpt_header( + return NULL; + } + +- /* Size of blocks with GPT entries */ +- esz = le32_to_cpu(h->num_partition_entries) * +- le32_to_cpu(h->sizeof_partition_entry); +- if (!esz) { ++ if (le32_to_cpu(h->num_partition_entries) == 0 || ++ le32_to_cpu(h->sizeof_partition_entry) == 0 || ++ ULONG_MAX / le32_to_cpu(h->num_partition_entries) < le32_to_cpu(h->sizeof_partition_entry)) { + DBG(LOWPROBE, blkid_debug("GPT entries undefined")); + return NULL; + } + ++ /* Size of blocks with GPT entries */ ++ esz = le32_to_cpu(h->num_partition_entries) * ++ le32_to_cpu(h->sizeof_partition_entry); ++ + /* The header seems valid, save it + * (we don't care about zeros in hdr->reserved2 area) */ + memcpy(hdr, h, sizeof(*h)); +diff --git a/libblkid/src/probe.c b/libblkid/src/probe.c +index f9fab5b..9cf099a 100644 +--- a/libblkid/src/probe.c ++++ b/libblkid/src/probe.c +@@ -103,6 +103,7 @@ + #include <inttypes.h> + #include <stdint.h> + #include <stdarg.h> ++#include <limits.h> + + #ifdef HAVE_LIBUUID + # include <uuid.h> +@@ -565,6 +566,12 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, + return NULL; + } + ++ /* someone trying to overflow some buffers? */ ++ if (len > ULONG_MAX - sizeof(struct blkid_bufinfo)) { ++ errno = ENOMEM; ++ return NULL; ++ } ++ + /* allocate info and space for data by why call */ + bf = calloc(1, sizeof(struct blkid_bufinfo) + len); + if (!bf) { +diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c +index 406ba2b..56ee472 100644 +--- a/libblkid/src/superblocks/zfs.c ++++ b/libblkid/src/superblocks/zfs.c +@@ -12,6 +12,7 @@ + #include <errno.h> + #include <ctype.h> + #include <inttypes.h> ++#include <limits.h> + + #include "superblocks.h" + +@@ -108,6 +109,8 @@ static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) + + nvs->nvs_type = be32_to_cpu(nvs->nvs_type); + nvs->nvs_strlen = be32_to_cpu(nvs->nvs_strlen); ++ if (nvs->nvs_strlen > UINT_MAX - sizeof(*nvs)) ++ break; + avail -= nvs->nvs_strlen + sizeof(*nvs); + nvdebug("nvstring: type %u string %*s\n", nvs->nvs_type, + nvs->nvs_strlen, nvs->nvs_string); +-- +2.9.3 + diff --git a/SOURCES/0096-umount-fix-obsolete-info-about-loop-in-umount.8.patch b/SOURCES/0096-umount-fix-obsolete-info-about-loop-in-umount.8.patch new file mode 100644 index 0000000..7d03314 --- /dev/null +++ b/SOURCES/0096-umount-fix-obsolete-info-about-loop-in-umount.8.patch @@ -0,0 +1,53 @@ +From ad1c931f24e81b169127d611eb8aac72665c4bf2 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 30 Aug 2016 10:25:44 +0200 +Subject: [PATCH 096/116] umount: fix obsolete info about loop= in umount.8 + +Upstream: Upstream: Upstream: https://github.com/karelzak/util-linux/commit/d00eb87ba284635e72b10880dfcf409276fa41b0 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1370959 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/umount.8 | 20 ++++++++++++-------- + 1 file changed, 12 insertions(+), 8 deletions(-) + +diff --git a/sys-utils/umount.8 b/sys-utils/umount.8 +index 1fa653c..f0a712b 100644 +--- a/sys-utils/umount.8 ++++ b/sys-utils/umount.8 +@@ -78,8 +78,10 @@ Do not canonicalize paths. For more details about this option see the + man page. + .TP + \fB\-d\fR, \fB\-\-detach\-loop\fR +-In case the unmounted device was a loop device, also free this loop +-device. ++When the unmounted device was a loop device, also free this loop ++device. This option is unnecessary for devices initialized by ++.BR mount (8), ++in this case "autoclear" functionality is enabled by default. + .TP + \fB\-\-fake\fP + Causes everything to be done except for the actual system call; this 'fakes' +@@ -145,12 +147,14 @@ Print version and exit. + .SH "THE LOOP DEVICE" + The + .B umount +-command will free the loop device (if any) associated with the mount, in +-case it finds the option 'loop=...' in +-.IR /etc/mtab , +-or when the \-d option was given. Any pending loop devices can be freed +-using 'losetup -d', see +-.BR losetup (8). ++command will automatically detach loop device previously initialized by ++.BR mount (8) ++command independently of /etc/mtab. ++ ++In this case the device is initialized with "autoclear" flag (see ++.BR losetup (8) ++output for more details), otherwise it's necessary to use the option \fB \-\-detach\-loop\fR ++or call \fBlosetup -d <device>\fR. The autoclear feature is supported since Linux 2.6.25. + .SH NOTES + The syntax of external umount helpers is: + .PP +-- +2.9.3 + diff --git a/SOURCES/0097-mount-fix-all-and-nofail-return-code.patch b/SOURCES/0097-mount-fix-all-and-nofail-return-code.patch new file mode 100644 index 0000000..0863b88 --- /dev/null +++ b/SOURCES/0097-mount-fix-all-and-nofail-return-code.patch @@ -0,0 +1,43 @@ +From d94c73b186ea4fec6333d1fb6cced1b4b8515d58 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 7 Apr 2014 11:53:05 +0200 +Subject: [PATCH 097/116] mount: fix --all and nofail return code + +Now the "nofail" affects warnings warning messages only. That's wrong +and regression (against original non-libmount version). The nofail has +to control return code too. + +Upstream: https://github.com/karelzak/util-linux/commit/8ab82185eed76bc20694a197fe10c5f9fb795b80 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1357746 +Reported-by: Patrick McLean <chutzpah@gentoo.org> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/mount.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/sys-utils/mount.c b/sys-utils/mount.c +index f332070..44e2b7c 100644 +--- a/sys-utils/mount.c ++++ b/sys-utils/mount.c +@@ -197,12 +197,14 @@ static int mount_all(struct libmnt_context *cxt) + if (mnt_context_is_verbose(cxt)) + printf("%-25s: mount successfully forked\n", tgt); + } else { +- mk_exit_code(cxt, mntrc); /* to print warnings */ +- +- if (mnt_context_get_status(cxt)) { ++ if (mk_exit_code(cxt, mntrc) == MOUNT_EX_SUCCESS) { + nsucc++; + +- if (mnt_context_is_verbose(cxt)) ++ /* Note that MOUNT_EX_SUCCESS return code does ++ * not mean that FS has been really mounted ++ * (e.g. nofail option) */ ++ if (mnt_context_get_status(cxt) ++ && mnt_context_is_verbose(cxt)) + printf("%-25s: successfully mounted\n", tgt); + } else + nerrs++; +-- +2.9.3 + diff --git a/SOURCES/0098-umount-exclude-selinuxfs-from-all.patch b/SOURCES/0098-umount-exclude-selinuxfs-from-all.patch new file mode 100644 index 0000000..aeb6279 --- /dev/null +++ b/SOURCES/0098-umount-exclude-selinuxfs-from-all.patch @@ -0,0 +1,28 @@ +From 92da0dea47ffceffb4a049a0a1094e41e212ceee Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 9 Feb 2017 11:21:49 +0100 +Subject: [PATCH 098/116] umount: exclude selinuxfs from --all + +Upstream: https://github.com/karelzak/util-linux/commit/9e66fd30d7bfbd7279db6100830aa9045d0255ca +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1417722 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/umount.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sys-utils/umount.c b/sys-utils/umount.c +index 9c47744..48483dc 100644 +--- a/sys-utils/umount.c ++++ b/sys-utils/umount.c +@@ -605,7 +605,7 @@ int main(int argc, char **argv) + + if (all) { + if (!types) +- types = "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd"; ++ types = "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd,noselinuxfs"; + + mnt_context_set_fstype_pattern(cxt, types); + rc = umount_all(cxt); +-- +2.9.3 + diff --git a/SOURCES/0099-sfdisk-remove-useless-CDROM-detection-for-s.patch b/SOURCES/0099-sfdisk-remove-useless-CDROM-detection-for-s.patch new file mode 100644 index 0000000..deca6be --- /dev/null +++ b/SOURCES/0099-sfdisk-remove-useless-CDROM-detection-for-s.patch @@ -0,0 +1,61 @@ +From 8229770ff79a383d7e5b133f94609e861aae4283 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 17 Mar 2017 15:03:28 +0100 +Subject: [PATCH 099/116] sfdisk: remove useless CDROM detection for -s + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1402825 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + fdisks/sfdisk.c | 16 ++++++---------- + 1 file changed, 6 insertions(+), 10 deletions(-) + +diff --git a/fdisks/sfdisk.c b/fdisks/sfdisk.c +index 93bca27..afb15fc 100644 +--- a/fdisks/sfdisk.c ++++ b/fdisks/sfdisk.c +@@ -2905,36 +2905,32 @@ do_pt_geom(char *dev, int silent) { + /* for compatibility with earlier fdisk: provide option -s */ + static void + do_size(char *dev, int silent) { +- int fd; ++ int fd, rc; + unsigned long long size; + + fd = my_open(dev, 0, silent); + if (fd < 0) + return; + +- if (blkdev_get_sectors(fd, &size) == -1) { ++ rc = blkdev_get_sectors(fd, &size); ++ close(fd); ++ ++ if (rc == -1) { + if (!silent) { + perror(dev); + errx(EXIT_FAILURE, _("Cannot get size of %s"), dev); + } +- goto done; ++ return; + } + + size /= 2; /* convert sectors to blocks */ + +- /* a CDROM drive without mounted CD yields MAXINT */ +- if (silent && size == ((1 << 30) - 1)) +- goto done; +- + if (silent) + printf("%s: %9llu\n", dev, size); + else + printf("%llu\n", size); + + total_size += size; +- +-done: +- close(fd); + } + + /* +-- +2.9.3 + diff --git a/SOURCES/0100-more-fix-repeat-search-crash.patch b/SOURCES/0100-more-fix-repeat-search-crash.patch new file mode 100644 index 0000000..edfd77f --- /dev/null +++ b/SOURCES/0100-more-fix-repeat-search-crash.patch @@ -0,0 +1,41 @@ +From d7e6a773863ad47dbdab9244b6629590d4b46f0e Mon Sep 17 00:00:00 2001 +From: Sami Kerola <kerolasa@iki.fi> +Date: Tue, 27 Jan 2015 22:28:57 +0000 +Subject: [PATCH 100/116] more: fix repeat search crash + +Repeating a search for a pattern that did not found made more(1) to +crash. To reproduce 'more /etc/services' and search for 'doom'; you will +find a service in port 666 - pressing '.' after that result used to cause +core dump. + +Upstream: https://github.com/karelzak/util-linux/commit/bc1ed338814f69473629b04c5e9efae5d7db3b72 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1403973 +Signed-off-by: Sami Kerola <kerolasa@iki.fi> +--- + text-utils/more.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/text-utils/more.c b/text-utils/more.c +index 496b116..0e9c2bd 100644 +--- a/text-utils/more.c ++++ b/text-utils/more.c +@@ -1588,6 +1588,8 @@ void search(char buf[], FILE *file, register int n) + context.line = saveln = Currline; + context.chrctr = startline; + lncount = 0; ++ if (!buf) ++ goto notfound; + if ((rc = regcomp(&re, buf, REG_NOSUB)) != 0) { + char s[REGERR_BUF]; + regerror(rc, &re, s, sizeof s); +@@ -1644,6 +1646,7 @@ void search(char buf[], FILE *file, register int n) + } + free(previousre); + previousre = NULL; ++notfound: + more_error(_("Pattern not found")); + } + } +-- +2.9.3 + diff --git a/SOURCES/0101-more-avoid-double-free-on-exit.patch b/SOURCES/0101-more-avoid-double-free-on-exit.patch new file mode 100644 index 0000000..87d1c36 --- /dev/null +++ b/SOURCES/0101-more-avoid-double-free-on-exit.patch @@ -0,0 +1,42 @@ +From f98c5f53d4661ec22097d36f5debd195491ec3c6 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 15 Dec 2016 14:40:26 +0100 +Subject: [PATCH 101/116] more: avoid double free() on exit + +On 'q' command more(1) calls end_it() function with _exit(). The +_exit() may suspend program execution due to pending I/O on very +loaded server. In this time SIGINT may be delivered due to impatient +user who will press ^C. + +And then end_it() cleanup function may be executed by signal handler +too. The result is double free()... + +Upstream: https://github.com/karelzak/util-linux/commit/0ed2a954714992938b35893b70197090a61b3b2e +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1403971 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + text-utils/more.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/text-utils/more.c b/text-utils/more.c +index 0e9c2bd..f98cb14 100644 +--- a/text-utils/more.c ++++ b/text-utils/more.c +@@ -763,6 +763,14 @@ void chgwinsz(int dummy __attribute__((__unused__))) + /* Clean up terminal state and exit. Also come here if interrupt signal received */ + void __attribute__((__noreturn__)) end_it(int dummy __attribute__((__unused__))) + { ++ /* May be executed as a signal handler as well as by main process. ++ * ++ * The _exit() may wait for pending I/O for really long time, be sure ++ * that signal handler is not executed in this time to avoid double ++ * de-initialization (free() calls, etc.). ++ */ ++ signal(SIGINT, SIG_IGN); ++ + reset_tty(); + if (clreol) { + putchar('\r'); +-- +2.9.3 + diff --git a/SOURCES/0102-ipcs-show-gid-instead-of-uid.patch b/SOURCES/0102-ipcs-show-gid-instead-of-uid.patch new file mode 100644 index 0000000..b549099 --- /dev/null +++ b/SOURCES/0102-ipcs-show-gid-instead-of-uid.patch @@ -0,0 +1,48 @@ +From a04fe4b347e563d7ff8d116fde6c6f26646ffadd Mon Sep 17 00:00:00 2001 +From: Hushan Jia <hushan.jia@gmail.com> +Date: Sun, 21 Feb 2016 21:10:24 -0800 +Subject: [PATCH 102/116] ipcs: show gid instead of uid + +ipcs -i incorrectly used uid where it should be gid + +Upstream: https://github.com/karelzak/util-linux/commit/e0bbe3d6ffed13ca5e4af450b8de775ddc5b30f3 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1358095 +Signed-off-by: Hushan Jia <hushan.jia@gmail.com> +--- + sys-utils/ipcs.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/sys-utils/ipcs.c b/sys-utils/ipcs.c +index 1843cd5..1db7fd0 100644 +--- a/sys-utils/ipcs.c ++++ b/sys-utils/ipcs.c +@@ -564,7 +564,7 @@ static void print_shm(int shmid, int unit) + + printf(_("\nShared memory Segment shmid=%d\n"), shmid); + printf(_("uid=%u\tgid=%u\tcuid=%u\tcgid=%u\n"), +- shmdata->shm_perm.uid, shmdata->shm_perm.uid, ++ shmdata->shm_perm.uid, shmdata->shm_perm.gid, + shmdata->shm_perm.cuid, shmdata->shm_perm.cgid); + printf(_("mode=%#o\taccess_perms=%#o\n"), shmdata->shm_perm.mode, + shmdata->shm_perm.mode & 0777); +@@ -594,7 +594,7 @@ void print_msg(int msgid, int unit) + + printf(_("\nMessage Queue msqid=%d\n"), msgid); + printf(_("uid=%u\tgid=%u\tcuid=%u\tcgid=%u\tmode=%#o\n"), +- msgdata->msg_perm.uid, msgdata->msg_perm.uid, ++ msgdata->msg_perm.uid, msgdata->msg_perm.gid, + msgdata->msg_perm.cuid, msgdata->msg_perm.cgid, + msgdata->msg_perm.mode); + ipc_print_size(unit, unit == IPC_UNIT_HUMAN ? _("csize=") : _("cbytes="), +@@ -627,7 +627,7 @@ static void print_sem(int semid) + + printf(_("\nSemaphore Array semid=%d\n"), semid); + printf(_("uid=%u\t gid=%u\t cuid=%u\t cgid=%u\n"), +- semdata->sem_perm.uid, semdata->sem_perm.uid, ++ semdata->sem_perm.uid, semdata->sem_perm.gid, + semdata->sem_perm.cuid, semdata->sem_perm.cgid); + printf(_("mode=%#o, access_perms=%#o\n"), + semdata->sem_perm.mode, semdata->sem_perm.mode & 0777); +-- +2.9.3 + diff --git a/SOURCES/0103-ipcs-fix-JP-status-message.patch b/SOURCES/0103-ipcs-fix-JP-status-message.patch new file mode 100644 index 0000000..91260b0 --- /dev/null +++ b/SOURCES/0103-ipcs-fix-JP-status-message.patch @@ -0,0 +1,27 @@ +From 68e4765ff497c6274c337a03874d2e4e8b04166d Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 20 Mar 2017 11:47:02 +0100 +Subject: [PATCH 103/116] ipcs: fix JP status message + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1358097 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + po/ja.po | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/po/ja.po b/po/ja.po +index 4c748de..f0d3645 100644 +--- a/po/ja.po ++++ b/po/ja.po +@@ -13013,7 +13013,7 @@ msgstr "セットされていません" + + #: sys-utils/ipcs.c:327 + msgid "dest" +-msgstr "対象" ++msgstr "削除" + + #: sys-utils/ipcs.c:328 + msgid "locked" +-- +2.9.3 + diff --git a/SOURCES/0104-swapon-fix-discard-option-parsing.patch b/SOURCES/0104-swapon-fix-discard-option-parsing.patch new file mode 100644 index 0000000..4de1ff6 --- /dev/null +++ b/SOURCES/0104-swapon-fix-discard-option-parsing.patch @@ -0,0 +1,44 @@ +From 294a277fceb5cd6640a9b7a6e8af7b2567150f34 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 20 Mar 2017 12:03:40 +0100 +Subject: [PATCH 104/116] swapon: fix discard option parsing + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1378100 +Upstream: https://github.com/karelzak/util-linux/commit/07332bfa1ec122a251194a62f91319841121d5aa +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/swapon.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/sys-utils/swapon.c b/sys-utils/swapon.c +index d85b910..d4f2c08 100644 +--- a/sys-utils/swapon.c ++++ b/sys-utils/swapon.c +@@ -632,20 +632,21 @@ static int swapon_all(void) + + while (mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0) { + /* defaults */ ++ size_t argsz = 0; + int pri = priority, dsc = discard, nofail = ifexists; + char *p, *src, *dscarg; + + if (mnt_fs_get_option(fs, "noauto", NULL, NULL) == 0) + continue; +- if (mnt_fs_get_option(fs, "discard", &dscarg, NULL) == 0) { ++ if (mnt_fs_get_option(fs, "discard", &dscarg, &argsz) == 0) { + dsc |= SWAP_FLAG_DISCARD; + if (dscarg) { + /* only single-time discards are wanted */ +- if (strcmp(dscarg, "once") == 0) ++ if (strncmp(dscarg, "once", argsz) == 0) + dsc |= SWAP_FLAG_DISCARD_ONCE; + + /* do discard for every released swap page */ +- if (strcmp(dscarg, "pages") == 0) ++ if (strncmp(dscarg, "pages", argsz) == 0) + dsc |= SWAP_FLAG_DISCARD_PAGES; + } + } +-- +2.9.3 + diff --git a/SOURCES/0105-fallocate-Added-posix_fallocate-support.patch b/SOURCES/0105-fallocate-Added-posix_fallocate-support.patch new file mode 100644 index 0000000..f200f20 --- /dev/null +++ b/SOURCES/0105-fallocate-Added-posix_fallocate-support.patch @@ -0,0 +1,207 @@ +From e3cfe198ebff9721efe6dd063da4b7b2dfe1d8b9 Mon Sep 17 00:00:00 2001 +From: Denis Chaplygin <dchaplyg@redhat.com> +Date: Tue, 31 Jan 2017 12:51:28 +0100 +Subject: [PATCH 105/116] fallocate: Added posix_fallocate() support. + +No all filesystems support Linux fallocate. The new option allow use +posix implementation if necessary. + +Upstream: https://github.com/karelzak/util-linux/commit/833f9a7aae713278eec5f85266597482f18c7370 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1416467 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + AUTHORS | 1 + + configure.ac | 28 ++++++++++++++++++++++++++++ + sys-utils/fallocate.1 | 10 ++++++++++ + sys-utils/fallocate.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- + 4 files changed, 81 insertions(+), 2 deletions(-) + +diff --git a/AUTHORS b/AUTHORS +index cd40985..3e02181 100644 +--- a/AUTHORS ++++ b/AUTHORS +@@ -148,6 +148,7 @@ CONTRIBUTORS: + David Miller <davem@davemloft.net> + David Prévot <david@tilapin.org> + David Woodhouse <dwmw2@infradead.org> ++ Denis Chaplygin <dchaplyg@redhat.com> + Denis ChengRq <crquan@gmail.com> + Dennis Gilmore <dennis@ausil.us> + Dennis Jensen <dennis.h.jensen@siemens.com> +diff --git a/configure.ac b/configure.ac +index 266ef08..f87a885 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -917,6 +917,34 @@ if test "x$build_fallocate" = xyes; then + AC_MSG_RESULT([no])]) + fi + ++AS_IF([test "x$build_fallocate" = xyes], [ ++ dnl check for valid posix_fallocate() function ++ AC_MSG_CHECKING([for valid posix_fallocate() function]) ++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ ++#ifdef HAVE_UNISTD_H ++# include <unistd.h> ++#endif ++#ifdef HAVE_SYS_TYPES_H ++# include <sys/types.h> ++#endif ++#ifdef HAVE_LINUX_FALLOC_H ++# include <linux/falloc.h> ++#endif ++#ifdef HAVE_FCNTL_H ++# include <fcntl.h> ++#endif ++]],[[ ++ long ret; ++ ret = posix_fallocate(0, 0xfffffffful, 0xfffffffful); ++ if (ret != 0) { ++ return 1; ++ } ++ ]])],[ ++ AC_MSG_RESULT([yes]) ++ AC_DEFINE([HAVE_POSIX_FALLOCATE], [1], [Have valid posix_fallocate() function])],[ ++ AC_MSG_RESULT([no])]) ++]) ++ + + AC_ARG_ENABLE([unshare], + AS_HELP_STRING([--disable-unshare], [do not build unshare]), +diff --git a/sys-utils/fallocate.1 b/sys-utils/fallocate.1 +index 49d26e4..3763530 100644 +--- a/sys-utils/fallocate.1 ++++ b/sys-utils/fallocate.1 +@@ -11,6 +11,13 @@ fallocate \- preallocate space to a file + .B \-l + .IR length + .I filename ++.PP ++.B fallocate \-x ++.RB [ \-o ++.IR offset ] ++.RB \-l ++.IR length ++.I filename + .SH DESCRIPTION + .B fallocate + is used to preallocate blocks to a file. For filesystems which support the +@@ -39,6 +46,9 @@ Punch holes in the file, the range should not exceed the length of the file. + Specifies the beginning offset of the allocation, in bytes. + .IP "\fB\-l, \-\-length\fP \fIlength\fP + Specifies the length of the allocation, in bytes. ++.IP "\fB\-x , \-\-posix\fP ++Enable POSIX operation mode. In that mode allocation operation always completes, ++but it may take longer time when fast allocation is not supported by the underlying filesystem. + .IP "\fB\-h, \-\-help\fP" + Print help and exit. + .IP "\fB-V, \-\-version" +diff --git a/sys-utils/fallocate.c b/sys-utils/fallocate.c +index ff0f9e6..17ae5fe 100644 +--- a/sys-utils/fallocate.c ++++ b/sys-utils/fallocate.c +@@ -52,6 +52,7 @@ + #include "strutils.h" + #include "c.h" + #include "closestream.h" ++#include "optutils.h" + + static void __attribute__((__noreturn__)) usage(FILE *out) + { +@@ -63,6 +64,9 @@ static void __attribute__((__noreturn__)) usage(FILE *out) + " -p, --punch-hole punch holes in the file\n" + " -o, --offset <num> offset of the allocation, in bytes\n" + " -l, --length <num> length of the allocation, in bytes\n"), out); ++#ifdef HAVE_POSIX_FALLOCATE ++ fputs(_(" -x, --posix use posix_fallocate(3) instead of fallocate(2)\n"), out); ++#endif + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); + fputs(USAGE_VERSION, out); +@@ -71,6 +75,18 @@ static void __attribute__((__noreturn__)) usage(FILE *out) + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); + } + ++ ++#ifdef HAVE_POSIX_FALLOCATE ++static void xposix_fallocate(int fd, off_t offset, off_t length) ++{ ++ int error = posix_fallocate(fd, offset, length); ++ if (error < 0) { ++ err(EXIT_FAILURE, _("fallocate failed")); ++ } ++} ++#endif ++ ++ + static loff_t cvtnum(char *s) + { + uintmax_t x; +@@ -85,9 +101,10 @@ int main(int argc, char **argv) + { + char *fname; + int c; +- int error; ++ int error = 0; + int fd; + int mode = 0; ++ int posix = 0; + loff_t length = -2LL; + loff_t offset = 0; + +@@ -98,15 +115,25 @@ int main(int argc, char **argv) + { "punch-hole", 0, 0, 'p' }, + { "offset", 1, 0, 'o' }, + { "length", 1, 0, 'l' }, ++ { "posix", 0, 0, 'x' }, + { NULL, 0, 0, 0 } + }; + ++ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ ++ { 'x', 'n', 'p' }, ++ { 0 } ++ }; ++ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; ++ + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + +- while ((c = getopt_long(argc, argv, "hVnpl:o:", longopts, NULL)) != -1) { ++ while ((c = getopt_long(argc, argv, "hVnpl:o:x", longopts, NULL)) != -1) { ++ ++ err_exclusive_options(c, longopts, excl, excl_st); ++ + switch(c) { + case 'h': + usage(stdout); +@@ -126,6 +153,13 @@ int main(int argc, char **argv) + case 'o': + offset = cvtnum(optarg); + break; ++ case 'x': ++#ifdef HAVE_POSIX_FALLOCATE ++ posix = 1; ++ break; ++#else ++ errx(EXIT_FAILURE, _("posix_fallocate support is not compiled")) ++#endif + default: + usage(stderr); + break; +@@ -152,6 +186,12 @@ int main(int argc, char **argv) + if (fd < 0) + err(EXIT_FAILURE, _("cannot open %s"), fname); + ++#ifdef HAVE_POSIX_FALLOCATE ++ if (posix) ++ xposix_fallocate(fd, offset, length); ++ else ++#endif ++ + #ifdef HAVE_FALLOCATE + error = fallocate(fd, mode, offset, length); + #else +-- +2.9.3 + diff --git a/SOURCES/0106-zramctl-backport-from-v2.29.patch b/SOURCES/0106-zramctl-backport-from-v2.29.patch new file mode 100644 index 0000000..2f87a48 --- /dev/null +++ b/SOURCES/0106-zramctl-backport-from-v2.29.patch @@ -0,0 +1,1723 @@ +From c43588bf03cc05b2eae724751b6652949e5c9caa Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 21 Mar 2017 13:04:17 +0100 +Subject: [PATCH 106/116] zramctl: backport from v2.29 + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1358755 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + .gitignore | 1 + + configure.ac | 6 + + include/Makemodule.am | 1 + + include/c.h | 8 + + include/strutils.h | 9 + + include/strv.h | 55 ++++ + include/sysfs.h | 3 + + lib/Makemodule.am | 3 +- + lib/strutils.c | 129 +++++++++ + lib/strv.c | 403 ++++++++++++++++++++++++++ + lib/sysfs.c | 45 ++- + sys-utils/Makemodule.am | 7 + + sys-utils/zramctl.8 | 123 ++++++++ + sys-utils/zramctl.c | 736 ++++++++++++++++++++++++++++++++++++++++++++++++ + 14 files changed, 1524 insertions(+), 5 deletions(-) + create mode 100644 include/strv.h + create mode 100644 lib/strv.c + create mode 100644 sys-utils/zramctl.8 + create mode 100644 sys-utils/zramctl.c + +diff --git a/configure.ac b/configure.ac +index f87a885..db7095a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -826,6 +826,12 @@ UL_REQUIRES_LINUX([losetup]) + AM_CONDITIONAL(BUILD_LOSETUP, test "x$build_losetup" = xyes) + + ++UL_BUILD_INIT([zramctl], [check]) ++UL_REQUIRES_LINUX([zramctl]) ++UL_REQUIRES_BUILD([zramctl], [libsmartcols]) ++AM_CONDITIONAL([BUILD_ZRAMCTL], [test "x$build_zramctl" = xyes]) ++ ++ + AC_ARG_ENABLE([cytune], + AS_HELP_STRING([--disable-cytune], [do not build cytune]), + [], enable_cytune=check +diff --git a/include/Makemodule.am b/include/Makemodule.am +index 757f317..1680296 100644 +--- a/include/Makemodule.am ++++ b/include/Makemodule.am +@@ -39,6 +39,7 @@ dist_noinst_HEADERS += \ + include/readutmp.h \ + include/setproctitle.h \ + include/strutils.h \ ++ include/strv.h \ + include/swapheader.h \ + include/sysfs.h \ + include/timer.h \ +diff --git a/include/c.h b/include/c.h +index a2779a5..3754e75 100644 +--- a/include/c.h ++++ b/include/c.h +@@ -309,6 +309,14 @@ static inline int usleep(useconds_t usec) + #endif + + /* ++ * Macros to convert #define'itions to strings, for example ++ * #define XYXXY 42 ++ * printf ("%s=%s\n", stringify(XYXXY), stringify_value(XYXXY)); ++ */ ++#define stringify_value(s) stringify(s) ++#define stringify(s) #s ++ ++/* + * Note that sysconf(_SC_GETPW_R_SIZE_MAX) returns *initial* suggested size for + * pwd buffer and in some cases it is not large enough. See POSIX and + * getpwnam_r man page for more details. +diff --git a/include/strutils.h b/include/strutils.h +index 709fcad..aa7b95f 100644 +--- a/include/strutils.h ++++ b/include/strutils.h +@@ -5,6 +5,7 @@ + #include <inttypes.h> + #include <string.h> + #include <sys/types.h> ++#include <stdio.h> + + /* default strtoxx_or_err() exit code */ + #ifndef STRTOXX_EXIT_CODE +@@ -102,4 +103,12 @@ extern int parse_range(const char *str, int *lower, int *upper, int def); + + extern int streq_except_trailing_slash(const char *s1, const char *s2); + ++extern char *strnappend(const char *s, const char *suffix, size_t b); ++extern char *strappend(const char *s, const char *suffix); ++extern char *strfappend(const char *s, const char *format, ...) ++ __attribute__ ((__format__ (__printf__, 2, 0))); ++extern const char *split(const char **state, size_t *l, const char *separator, int quoted); ++ ++extern int skip_fline(FILE *fp); ++ + #endif +diff --git a/include/strv.h b/include/strv.h +new file mode 100644 +index 0000000..260ad12 +--- /dev/null ++++ b/include/strv.h +@@ -0,0 +1,55 @@ ++#ifndef UTIL_LINUX_STRV ++#define UTIL_LINUX_STRV ++ ++#include <stdarg.h> ++ ++#include "c.h" ++ ++char **strv_free(char **l); ++void strv_clear(char **l); ++char **strv_copy(char * const *l); ++unsigned strv_length(char * const *l); ++ ++int strv_extend_strv(char ***a, char **b); ++int strv_extend_strv_concat(char ***a, char **b, const char *suffix); ++int strv_extend(char ***l, const char *value); ++int strv_extendv(char ***l, const char *format, va_list ap); ++int strv_extendf(char ***l, const char *format, ...) ++ __attribute__ ((__format__ (__printf__, 2, 0))); ++int strv_push(char ***l, char *value); ++int strv_push_prepend(char ***l, char *value); ++int strv_consume(char ***l, char *value); ++int strv_consume_prepend(char ***l, char *value); ++ ++char **strv_remove(char **l, const char *s); ++ ++char **strv_new(const char *x, ...); ++char **strv_new_ap(const char *x, va_list ap); ++ ++static inline const char* STRV_IFNOTNULL(const char *x) { ++ return x ? x : (const char *) -1; ++} ++ ++static inline int strv_isempty(char * const *l) { ++ return !l || !*l; ++} ++ ++char **strv_split(const char *s, const char *separator); ++char *strv_join(char **l, const char *separator); ++ ++#define STRV_FOREACH(s, l) \ ++ for ((s) = (l); (s) && *(s); (s)++) ++ ++#define STRV_FOREACH_BACKWARDS(s, l) \ ++ STRV_FOREACH(s, l) \ ++ ; \ ++ for ((s)--; (l) && ((s) >= (l)); (s)--) ++ ++ ++#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) ++ ++char **strv_reverse(char **l); ++ ++#endif /* UTIL_LINUX_STRV */ ++ ++ +diff --git a/include/sysfs.h b/include/sysfs.h +index 0a9c218..a547005 100644 +--- a/include/sysfs.h ++++ b/include/sysfs.h +@@ -58,6 +58,9 @@ extern int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res) + extern int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res); + extern int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res); + ++extern int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str); ++extern int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num); ++ + extern char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz); + + extern char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr); +diff --git a/lib/Makemodule.am b/lib/Makemodule.am +index 73280f9..faf9d74 100644 +--- a/lib/Makemodule.am ++++ b/lib/Makemodule.am +@@ -27,7 +27,8 @@ libcommon_la_SOURCES = \ + lib/ttyutils.c \ + lib/xgetpass.c \ + lib/exec_shell.c \ +- lib/readutmp.c ++ lib/readutmp.c \ ++ lib/strv.c + + if LINUX + libcommon_la_SOURCES += \ +diff --git a/lib/strutils.c b/lib/strutils.c +index f9cdcbb..4b8a813 100644 +--- a/lib/strutils.c ++++ b/lib/strutils.c +@@ -10,6 +10,7 @@ + #include <errno.h> + #include <sys/stat.h> + #include <string.h> ++#include <assert.h> + + #include "c.h" + #include "nls.h" +@@ -687,6 +688,134 @@ int streq_except_trailing_slash(const char *s1, const char *s2) + return equal; + } + ++char *strnappend(const char *s, const char *suffix, size_t b) ++{ ++ size_t a; ++ char *r; ++ ++ if (!s && !suffix) ++ return strdup(""); ++ if (!s) ++ return strndup(suffix, b); ++ if (!suffix) ++ return strdup(s); ++ ++ assert(s); ++ assert(suffix); ++ ++ a = strlen(s); ++ if (b > ((size_t) -1) - a) ++ return NULL; ++ ++ r = malloc(a + b + 1); ++ if (!r) ++ return NULL; ++ ++ memcpy(r, s, a); ++ memcpy(r + a, suffix, b); ++ r[a+b] = 0; ++ ++ return r; ++} ++ ++char *strappend(const char *s, const char *suffix) ++{ ++ return strnappend(s, suffix, suffix ? strlen(suffix) : 0); ++} ++ ++char *strfappend(const char *s, const char *format, ...) ++{ ++ va_list ap; ++ char *val, *res; ++ int sz; ++ ++ va_start(ap, format); ++ sz = vasprintf(&val, format, ap); ++ va_end(ap); ++ ++ if (sz < 0) ++ return NULL; ++ ++ res = strnappend(s, val, sz); ++ free(val); ++ return res; ++} ++ ++static size_t strcspn_escaped(const char *s, const char *reject) ++{ ++ int escaped = 0; ++ int n; ++ ++ for (n=0; s[n]; n++) { ++ if (escaped) ++ escaped = 0; ++ else if (s[n] == '\\') ++ escaped = 1; ++ else if (strchr(reject, s[n])) ++ break; ++ } ++ ++ /* if s ends in \, return index of previous char */ ++ return n - escaped; ++} ++ ++/* Split a string into words. */ ++const char *split(const char **state, size_t *l, const char *separator, int quoted) ++{ ++ const char *current; ++ ++ current = *state; ++ ++ if (!*current) { ++ assert(**state == '\0'); ++ return NULL; ++ } ++ ++ current += strspn(current, separator); ++ if (!*current) { ++ *state = current; ++ return NULL; ++ } ++ ++ if (quoted && strchr("\'\"", *current)) { ++ char quotechars[2] = {*current, '\0'}; ++ ++ *l = strcspn_escaped(current + 1, quotechars); ++ if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || ++ (current[*l + 2] && !strchr(separator, current[*l + 2]))) { ++ /* right quote missing or garbage at the end */ ++ *state = current; ++ return NULL; ++ } ++ *state = current++ + *l + 2; ++ } else if (quoted) { ++ *l = strcspn_escaped(current, separator); ++ if (current[*l] && !strchr(separator, current[*l])) { ++ /* unfinished escape */ ++ *state = current; ++ return NULL; ++ } ++ *state = current + *l; ++ } else { ++ *l = strcspn(current, separator); ++ *state = current + *l; ++ } ++ ++ return current; ++} ++ ++/* Rewind file pointer forward to new line. */ ++int skip_fline(FILE *fp) ++{ ++ int ch; ++ ++ do { ++ if ((ch = fgetc(fp)) == EOF) ++ return 1; ++ if (ch == '\n') ++ return 0; ++ } while (1); ++} + + #ifdef TEST_PROGRAM + +diff --git a/lib/strv.c b/lib/strv.c +new file mode 100644 +index 0000000..ddc2a0c +--- /dev/null ++++ b/lib/strv.c +@@ -0,0 +1,403 @@ ++/* ++ * ++ * Copyright 2010 Lennart Poettering ++ * ++ * This 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. ++ * ++ * ++ * Copyright (C) 2015 Karel Zak <kzak@redhat.com> ++ * Modified the original version from systemd project for util-linux. ++ */ ++ ++#include <stdlib.h> ++#include <stdarg.h> ++#include <string.h> ++#include <errno.h> ++#include <stdbool.h> ++#include <assert.h> ++ ++#include "strutils.h" ++#include "strv.h" ++ ++void strv_clear(char **l) { ++ char **k; ++ ++ if (!l) ++ return; ++ ++ for (k = l; *k; k++) ++ free(*k); ++ ++ *l = NULL; ++} ++ ++char **strv_free(char **l) { ++ strv_clear(l); ++ free(l); ++ return NULL; ++} ++ ++char **strv_copy(char * const *l) { ++ char **r, **k; ++ ++ k = r = malloc(sizeof(char *) * (strv_length(l) + 1)); ++ if (!r) ++ return NULL; ++ ++ if (l) ++ for (; *l; k++, l++) { ++ *k = strdup(*l); ++ if (!*k) { ++ strv_free(r); ++ return NULL; ++ } ++ } ++ ++ *k = NULL; ++ return r; ++} ++ ++unsigned strv_length(char * const *l) { ++ unsigned n = 0; ++ ++ if (!l) ++ return 0; ++ ++ for (; *l; l++) ++ n++; ++ ++ return n; ++} ++ ++char **strv_new_ap(const char *x, va_list ap) { ++ const char *s; ++ char **a; ++ unsigned n = 0, i = 0; ++ va_list aq; ++ ++ /* As a special trick we ignore all listed strings that equal ++ * (const char*) -1. This is supposed to be used with the ++ * STRV_IFNOTNULL() macro to include possibly NULL strings in ++ * the string list. */ ++ ++ if (x) { ++ n = x == (const char*) -1 ? 0 : 1; ++ ++ va_copy(aq, ap); ++ while ((s = va_arg(aq, const char*))) { ++ if (s == (const char*) -1) ++ continue; ++ ++ n++; ++ } ++ ++ va_end(aq); ++ } ++ ++ a = malloc(sizeof(char *) * (n + 1)); ++ if (!a) ++ return NULL; ++ ++ if (x) { ++ if (x != (const char*) -1) { ++ a[i] = strdup(x); ++ if (!a[i]) ++ goto fail; ++ i++; ++ } ++ ++ while ((s = va_arg(ap, const char*))) { ++ ++ if (s == (const char*) -1) ++ continue; ++ ++ a[i] = strdup(s); ++ if (!a[i]) ++ goto fail; ++ ++ i++; ++ } ++ } ++ ++ a[i] = NULL; ++ ++ return a; ++ ++fail: ++ strv_free(a); ++ return NULL; ++} ++ ++char **strv_new(const char *x, ...) { ++ char **r; ++ va_list ap; ++ ++ va_start(ap, x); ++ r = strv_new_ap(x, ap); ++ va_end(ap); ++ ++ return r; ++} ++ ++int strv_extend_strv(char ***a, char **b) { ++ int r; ++ char **s; ++ ++ STRV_FOREACH(s, b) { ++ r = strv_extend(a, *s); ++ if (r < 0) ++ return r; ++ } ++ ++ return 0; ++} ++ ++int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { ++ int r; ++ char **s; ++ ++ STRV_FOREACH(s, b) { ++ char *v; ++ ++ v = strappend(*s, suffix); ++ if (!v) ++ return -ENOMEM; ++ ++ r = strv_push(a, v); ++ if (r < 0) { ++ free(v); ++ return r; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++#define _FOREACH_WORD(word, length, s, separator, quoted, state) \ ++ for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) ++ ++#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ ++ _FOREACH_WORD(word, length, s, separator, false, state) ++ ++ ++char **strv_split(const char *s, const char *separator) { ++ const char *word, *state; ++ size_t l; ++ unsigned n, i; ++ char **r; ++ ++ assert(s); ++ ++ n = 0; ++ FOREACH_WORD_SEPARATOR(word, l, s, separator, state) ++ n++; ++ ++ r = malloc(sizeof(char *) * (n + 1)); ++ if (!r) ++ return NULL; ++ ++ i = 0; ++ FOREACH_WORD_SEPARATOR(word, l, s, separator, state) { ++ r[i] = strndup(word, l); ++ if (!r[i]) { ++ strv_free(r); ++ return NULL; ++ } ++ ++ i++; ++ } ++ ++ r[i] = NULL; ++ return r; ++} ++ ++char *strv_join(char **l, const char *separator) { ++ char *r, *e; ++ char **s; ++ size_t n, k; ++ ++ if (!separator) ++ separator = " "; ++ ++ k = strlen(separator); ++ ++ n = 0; ++ STRV_FOREACH(s, l) { ++ if (n != 0) ++ n += k; ++ n += strlen(*s); ++ } ++ ++ r = malloc(n + 1); ++ if (!r) ++ return NULL; ++ ++ e = r; ++ STRV_FOREACH(s, l) { ++ if (e != r) ++ e = stpcpy(e, separator); ++ ++ e = stpcpy(e, *s); ++ } ++ ++ *e = 0; ++ ++ return r; ++} ++ ++int strv_push(char ***l, char *value) { ++ char **c; ++ unsigned n, m; ++ ++ if (!value) ++ return 0; ++ ++ n = strv_length(*l); ++ ++ /* Increase and check for overflow */ ++ m = n + 2; ++ if (m < n) ++ return -ENOMEM; ++ ++ c = realloc(*l, sizeof(char *) * m); ++ if (!c) ++ return -ENOMEM; ++ ++ c[n] = value; ++ c[n+1] = NULL; ++ ++ *l = c; ++ return 0; ++} ++ ++int strv_push_prepend(char ***l, char *value) { ++ char **c; ++ unsigned n, m, i; ++ ++ if (!value) ++ return 0; ++ ++ n = strv_length(*l); ++ ++ /* increase and check for overflow */ ++ m = n + 2; ++ if (m < n) ++ return -ENOMEM; ++ ++ c = malloc(sizeof(char *) * m); ++ if (!c) ++ return -ENOMEM; ++ ++ for (i = 0; i < n; i++) ++ c[i+1] = (*l)[i]; ++ ++ c[0] = value; ++ c[n+1] = NULL; ++ ++ free(*l); ++ *l = c; ++ ++ return 0; ++} ++ ++int strv_consume(char ***l, char *value) { ++ int r; ++ ++ r = strv_push(l, value); ++ if (r < 0) ++ free(value); ++ ++ return r; ++} ++ ++int strv_consume_prepend(char ***l, char *value) { ++ int r; ++ ++ r = strv_push_prepend(l, value); ++ if (r < 0) ++ free(value); ++ ++ return r; ++} ++ ++int strv_extend(char ***l, const char *value) { ++ char *v; ++ ++ if (!value) ++ return 0; ++ ++ v = strdup(value); ++ if (!v) ++ return -ENOMEM; ++ ++ return strv_consume(l, v); ++} ++ ++char **strv_remove(char **l, const char *s) { ++ char **f, **t; ++ ++ if (!l) ++ return NULL; ++ ++ assert(s); ++ ++ /* Drops every occurrence of s in the string list, edits ++ * in-place. */ ++ ++ for (f = t = l; *f; f++) ++ if (strcmp(*f, s) == 0) ++ free(*f); ++ else ++ *(t++) = *f; ++ ++ *t = NULL; ++ return l; ++} ++ ++int strv_extendf(char ***l, const char *format, ...) { ++ va_list ap; ++ char *x; ++ int r; ++ ++ va_start(ap, format); ++ r = vasprintf(&x, format, ap); ++ va_end(ap); ++ ++ if (r < 0) ++ return -ENOMEM; ++ ++ return strv_consume(l, x); ++} ++ ++int strv_extendv(char ***l, const char *format, va_list ap) { ++ char *x; ++ int r; ++ ++ r = vasprintf(&x, format, ap); ++ if (r < 0) ++ return -ENOMEM; ++ ++ return strv_consume(l, x); ++} ++ ++char **strv_reverse(char **l) { ++ unsigned n, i; ++ ++ n = strv_length(l); ++ if (n <= 1) ++ return l; ++ ++ for (i = 0; i < n / 2; i++) { ++ char *t; ++ ++ t = l[i]; ++ l[i] = l[n-1-i]; ++ l[n-1-i] = t; ++ } ++ ++ return l; ++} +diff --git a/lib/sysfs.c b/lib/sysfs.c +index 0bfd622..65a8394 100644 +--- a/lib/sysfs.c ++++ b/lib/sysfs.c +@@ -10,6 +10,7 @@ + #include "at.h" + #include "pathnames.h" + #include "sysfs.h" ++#include "all-io.h" + + char *sysfs_devno_attribute_path(dev_t devno, char *buf, + size_t bufsiz, const char *attr) +@@ -203,9 +204,9 @@ int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr) + return sysfs_stat(cxt, attr, &st) == 0; + } + +-static int sysfs_open(struct sysfs_cxt *cxt, const char *attr) ++static int sysfs_open(struct sysfs_cxt *cxt, const char *attr, int flags) + { +- int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, O_RDONLY|O_CLOEXEC); ++ int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, flags); + + if (fd == -1 && errno == ENOENT && + strncmp(attr, "queue/", 6) == 0 && cxt->parent) { +@@ -238,7 +239,7 @@ DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr) + int fd = -1; + + if (attr) +- fd = sysfs_open(cxt, attr); ++ fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC); + + else if (cxt->dir_fd >= 0) + /* request to open root of device in sysfs (/sys/block/<dev>) +@@ -263,7 +264,7 @@ DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr) + + static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr) + { +- int fd = sysfs_open(cxt, attr); ++ int fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC); + + return fd < 0 ? NULL : fdopen(fd, "r" UL_CLOEXECSTR); + } +@@ -417,6 +418,42 @@ int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res) + return -1; + } + ++int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str) ++{ ++ int fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC); ++ int rc, errsv; ++ ++ if (fd < 0) ++ return -errno; ++ rc = write_all(fd, str, strlen(str)); ++ ++ errsv = errno; ++ close(fd); ++ errno = errsv; ++ return rc; ++} ++ ++int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num) ++{ ++ char buf[sizeof(stringify_value(ULLONG_MAX))]; ++ int fd, rc = 0, len, errsv; ++ ++ fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC); ++ if (fd < 0) ++ return -errno; ++ ++ len = snprintf(buf, sizeof(buf), "%" PRIu64, num); ++ if (len < 0 || (size_t) len >= sizeof(buf)) ++ rc = len < 0 ? -errno : -E2BIG; ++ else ++ rc = write_all(fd, buf, len); ++ ++ errsv = errno; ++ close(fd); ++ errno = errsv; ++ return rc; ++} ++ + char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr) + { + char buf[1024]; +diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am +index 6badd17..408e884 100644 +--- a/sys-utils/Makemodule.am ++++ b/sys-utils/Makemodule.am +@@ -184,6 +184,13 @@ losetup_static_LDADD = $(losetup_LDADD) + endif + endif # BUILD_LOSETUP + ++if BUILD_ZRAMCTL ++sbin_PROGRAMS += zramctl ++dist_man_MANS += sys-utils/zramctl.8 ++zramctl_SOURCES = sys-utils/zramctl.c ++zramctl_LDADD = $(LDADD) libcommon.la libsmartcols.la ++zramctl_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) ++endif + + if BUILD_PRLIMIT + usrbin_exec_PROGRAMS += prlimit +diff --git a/sys-utils/zramctl.8 b/sys-utils/zramctl.8 +new file mode 100644 +index 0000000..f6fc45c +--- /dev/null ++++ b/sys-utils/zramctl.8 +@@ -0,0 +1,123 @@ ++.TH ZRAMCTL 8 "July 2014" "util-linux" "System Administration" ++.SH NAME ++zramctl \- set up and control zram devices ++.SH SYNOPSIS ++.ad l ++Get info: ++.sp ++.in +5 ++.BR zramctl " [options]" ++.sp ++.in -5 ++Reset zram: ++.sp ++.in +5 ++.B "zramctl \-r" ++.IR zramdev ... ++.sp ++.in -5 ++Print name of first unused zram device: ++.sp ++.in +5 ++.B "zramctl \-f" ++.sp ++.in -5 ++Set up a zram device: ++.sp ++.in +5 ++.B zramctl ++.RB [ \-f " | "\fIzramdev\fP ] ++.RB [ \-s ++.IR size ] ++.RB [ \-t ++.IR number ] ++.RB [ \-a ++.IR algorithm ] ++.sp ++.in -5 ++.ad b ++.SH DESCRIPTION ++.B zramctl ++is used to quickly set up zram device parameters, to reset zram devices, and to ++query the status of used zram devices. If no option is given, all zram devices ++are shown. ++ ++.SH OPTIONS ++.TP ++.BR \-a , " \-\-algorithm lzo" | lz4 ++Set the compression algorithm to be used for compressing data in the zram device. ++.TP ++.BR \-f , " \-\-find" ++Find the first unused zram device. If a \fB--size\fR argument is present, then ++initialize the device. ++.TP ++.BR \-n , " \-\-noheadings" ++Do not print a header line in status output. ++.TP ++.BR \-o , " \-\-output " \fIlist ++Define the status output columns to be used. If no output arrangement is ++specified, then a default set is used. ++Use \fB\-\-help\fP to get a list of all supported columns. ++.TP ++.B \-\-raw ++Use the raw format for status output. ++.TP ++.BR \-r , " \-\-reset" ++Reset the options of the specified zram device(s). Zram device settings ++can be changed only after a reset. ++.TP ++.BR \-s , " \-\-size " \fIsize ++Create a zram device of the specified \fIsize\fR. ++Zram devices are aligned to memory pages; when the requested \fIsize\fR is ++not a multiple of the page size, it will be rounded up to the next multiple. ++When not otherwise specified, the unit of the \fIsize\fR parameter is bytes. ++.IP ++The \fIsize\fR argument 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. ++.TP ++.BR \-t , " \-\-streams " \fInumber ++Set the maximum number of compression streams that can be used for the device. ++The default is one stream. ++.TP ++.BR \-V , " \-\-version" ++Display version information and exit. ++.TP ++.BR \-h , " \-\-help" ++Display help text and exit. ++ ++.SH RETURN VALUE ++.B zramctl ++returns 0 on success, nonzero on failure. ++ ++.SH FILES ++.TP ++.I /dev/zram[0..N] ++zram block devices ++ ++.SH EXAMPLE ++The following commands set up a zram device with a size of one gigabyte ++and use it as swap device. ++.nf ++.IP ++# zramctl --find --size 1024M ++/dev/zram0 ++# mkswap /dev/zram0 ++# swapon /dev/zram0 ++ ... ++# swapoff /dev/zram0 ++# zramctl --reset /dev/zram0 ++.fi ++.SH SEE ALSO ++.UR http://git.\:kernel.\:org\:/cgit\:/linux\:/kernel\:/git\:/torvalds\:/linux.git\:/tree\:/Documentation\:/blockdev\:/zram.txt ++Linux kernel documentation ++.UE . ++.SH AUTHORS ++.nf ++Timofey Titovets <nefelim4ag@gmail.com> ++Karel Zak <kzak@redhat.com> ++.fi ++.SH AVAILABILITY ++The zramctl command is part of the util-linux package and is available from ++https://www.kernel.org/pub/linux/utils/util-linux/. +diff --git a/sys-utils/zramctl.c b/sys-utils/zramctl.c +new file mode 100644 +index 0000000..853401c +--- /dev/null ++++ b/sys-utils/zramctl.c +@@ -0,0 +1,736 @@ ++/* ++ * zramctl - control compressed block devices in RAM ++ * ++ * Copyright (c) 2014 Timofey Titovets <Nefelim4ag@gmail.com> ++ * Copyright (C) 2014 Karel Zak <kzak@redhat.com> ++ * ++ * 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. ++ * ++ * 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 <getopt.h> ++#include <stdlib.h> ++#include <string.h> ++#include <stdarg.h> ++#include <assert.h> ++ ++#include <libsmartcols.h> ++ ++#include "c.h" ++#include "nls.h" ++#include "closestream.h" ++#include "strutils.h" ++#include "xalloc.h" ++#include "sysfs.h" ++#include "optutils.h" ++#include "ismounted.h" ++#include "strv.h" ++#include "path.h" ++#include "pathnames.h" ++ ++/*#define CONFIG_ZRAM_DEBUG*/ ++ ++#ifdef CONFIG_ZRAM_DEBUG ++# define DBG(x) do { fputs("zram: ", stderr); x; fputc('\n', stderr); } while(0) ++#else ++# define DBG(x) ++#endif ++ ++/* status output columns */ ++struct colinfo { ++ const char *name; ++ double whint; ++ int flags; ++ const char *help; ++}; ++ ++enum { ++ COL_NAME = 0, ++ COL_DISKSIZE, ++ COL_ORIG_SIZE, ++ COL_COMP_SIZE, ++ COL_ALGORITHM, ++ COL_STREAMS, ++ COL_ZEROPAGES, ++ COL_MEMTOTAL, ++ COL_MEMLIMIT, ++ COL_MEMUSED, ++ COL_MIGRATED, ++ COL_MOUNTPOINT ++}; ++ ++static const struct colinfo infos[] = { ++ [COL_NAME] = { "NAME", 0.25, 0, N_("zram device name") }, ++ [COL_DISKSIZE] = { "DISKSIZE", 5, SCOLS_FL_RIGHT, N_("limit on the uncompressed amount of data") }, ++ [COL_ORIG_SIZE] = { "DATA", 5, SCOLS_FL_RIGHT, N_("uncompressed size of stored data") }, ++ [COL_COMP_SIZE] = { "COMPR", 5, SCOLS_FL_RIGHT, N_("compressed size of stored data") }, ++ [COL_ALGORITHM] = { "ALGORITHM", 3, 0, N_("the selected compression algorithm") }, ++ [COL_STREAMS] = { "STREAMS", 3, SCOLS_FL_RIGHT, N_("number of concurrent compress operations") }, ++ [COL_ZEROPAGES] = { "ZERO-PAGES", 3, SCOLS_FL_RIGHT, N_("empty pages with no allocated memory") }, ++ [COL_MEMTOTAL] = { "TOTAL", 5, SCOLS_FL_RIGHT, N_("all memory including allocator fragmentation and metadata overhead") }, ++ [COL_MEMLIMIT] = { "MEM-LIMIT", 5, SCOLS_FL_RIGHT, N_("memory limit used to store compressed data") }, ++ [COL_MEMUSED] = { "MEM-USED", 5, SCOLS_FL_RIGHT, N_("memory zram have been consumed to store compressed data") }, ++ [COL_MIGRATED] = { "MIGRATED", 5, SCOLS_FL_RIGHT, N_("number of objects migrated by compaction") }, ++ [COL_MOUNTPOINT]= { "MOUNTPOINT",0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") }, ++}; ++ ++static int columns[ARRAY_SIZE(infos) * 2] = {-1}; ++static int ncolumns; ++ ++enum { ++ MM_ORIG_DATA_SIZE = 0, ++ MM_COMPR_DATA_SIZE, ++ MM_MEM_USED_TOTAL, ++ MM_MEM_LIMIT, ++ MM_MEM_USED_MAX, ++ MM_ZERO_PAGES, ++ MM_NUM_MIGRATED ++}; ++ ++static const char *mm_stat_names[] = { ++ [MM_ORIG_DATA_SIZE] = "orig_data_size", ++ [MM_COMPR_DATA_SIZE] = "compr_data_size", ++ [MM_MEM_USED_TOTAL] = "mem_used_total", ++ [MM_MEM_LIMIT] = "mem_limit", ++ [MM_MEM_USED_MAX] = "mem_used_max", ++ [MM_ZERO_PAGES] = "zero_pages", ++ [MM_NUM_MIGRATED] = "num_migrated" ++}; ++ ++ ++struct zram { ++ char devname[32]; ++ struct sysfs_cxt sysfs; ++ char **mm_stat; ++ ++ unsigned int mm_stat_probed : 1, ++ control_probed : 1, ++ has_control : 1; /* has /sys/class/zram-control/ */ ++}; ++ ++#define ZRAM_EMPTY { .devname = { '\0' }, .sysfs = UL_SYSFSCXT_EMPTY } ++ ++static unsigned int raw, no_headings, inbytes; ++ ++ ++static int get_column_id(int num) ++{ ++ assert(num < ncolumns); ++ assert(columns[num] < (int) ARRAY_SIZE(infos)); ++ return columns[num]; ++} ++ ++static const struct colinfo *get_column_info(int num) ++{ ++ return &infos[ get_column_id(num) ]; ++} ++ ++static int column_name_to_id(const char *name, size_t namesz) ++{ ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(infos); i++) { ++ const char *cn = infos[i].name; ++ ++ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) ++ return i; ++ } ++ warnx(_("unknown column: %s"), name); ++ return -1; ++} ++ ++static void zram_reset_stat(struct zram *z) ++{ ++ if (z) { ++ strv_free(z->mm_stat); ++ z->mm_stat = NULL; ++ z->mm_stat_probed = 0; ++ } ++} ++ ++static void zram_set_devname(struct zram *z, const char *devname, size_t n) ++{ ++ assert(z); ++ ++ if (!devname) ++ snprintf(z->devname, sizeof(z->devname), "/dev/zram%zu", n); ++ else { ++ strncpy(z->devname, devname, sizeof(z->devname)); ++ z->devname[sizeof(z->devname) - 1] = '\0'; ++ } ++ ++ DBG(fprintf(stderr, "set devname: %s", z->devname)); ++ sysfs_deinit(&z->sysfs); ++ zram_reset_stat(z); ++} ++ ++static int zram_get_devnum(struct zram *z) ++{ ++ int n; ++ ++ assert(z); ++ ++ if (sscanf(z->devname, "/dev/zram%d", &n) == 1) ++ return n; ++ return -EINVAL; ++} ++ ++static struct zram *new_zram(const char *devname) ++{ ++ struct zram *z = xcalloc(1, sizeof(struct zram)); ++ ++ DBG(fprintf(stderr, "new: %p", z)); ++ if (devname) ++ zram_set_devname(z, devname, 0); ++ return z; ++} ++ ++static void free_zram(struct zram *z) ++{ ++ if (!z) ++ return; ++ DBG(fprintf(stderr, "free: %p", z)); ++ sysfs_deinit(&z->sysfs); ++ zram_reset_stat(z); ++ free(z); ++} ++ ++static struct sysfs_cxt *zram_get_sysfs(struct zram *z) ++{ ++ assert(z); ++ ++ if (!z->sysfs.devno) { ++ dev_t devno = sysfs_devname_to_devno(z->devname, NULL); ++ if (!devno) ++ return NULL; ++ if (sysfs_init(&z->sysfs, devno, NULL)) ++ return NULL; ++ if (*z->devname != '/') { ++ /* canonicalize the device name according to /sys */ ++ char name[PATH_MAX]; ++ if (sysfs_get_devname(&z->sysfs, name, sizeof(name))) ++ snprintf(z->devname, sizeof(z->devname), "/dev/%s", name); ++ } ++ } ++ ++ return &z->sysfs; ++} ++ ++static inline int zram_exist(struct zram *z) ++{ ++ assert(z); ++ ++ errno = 0; ++ if (zram_get_sysfs(z) == NULL) { ++ errno = ENODEV; ++ return 0; ++ } ++ ++ DBG(fprintf(stderr, "%s exists", z->devname)); ++ return 1; ++} ++ ++static int zram_set_u64parm(struct zram *z, const char *attr, uint64_t num) ++{ ++ struct sysfs_cxt *sysfs = zram_get_sysfs(z); ++ if (!sysfs) ++ return -EINVAL; ++ DBG(fprintf(stderr, "%s writing %ju to %s", z->devname, num, attr)); ++ return sysfs_write_u64(sysfs, attr, num); ++} ++ ++static int zram_set_strparm(struct zram *z, const char *attr, const char *str) ++{ ++ struct sysfs_cxt *sysfs = zram_get_sysfs(z); ++ if (!sysfs) ++ return -EINVAL; ++ DBG(fprintf(stderr, "%s writing %s to %s", z->devname, str, attr)); ++ return sysfs_write_string(sysfs, attr, str); ++} ++ ++ ++static int zram_used(struct zram *z) ++{ ++ uint64_t size; ++ struct sysfs_cxt *sysfs = zram_get_sysfs(z); ++ ++ if (sysfs && ++ sysfs_read_u64(sysfs, "disksize", &size) == 0 && ++ size > 0) { ++ ++ DBG(fprintf(stderr, "%s used", z->devname)); ++ return 1; ++ } ++ DBG(fprintf(stderr, "%s unused", z->devname)); ++ return 0; ++} ++ ++static int zram_has_control(struct zram *z) ++{ ++ if (!z->control_probed) { ++ z->has_control = access(_PATH_SYS_CLASS "/zram-control/", F_OK) == 0 ? 1 : 0; ++ z->control_probed = 1; ++ DBG(fprintf(stderr, "zram-control: %s", z->has_control ? "yes" : "no")); ++ } ++ ++ return z->has_control; ++} ++ ++static int zram_control_add(struct zram *z) ++{ ++ int n; ++ ++ if (!zram_has_control(z)) ++ return -ENOSYS; ++ ++ n = path_read_s32(_PATH_SYS_CLASS "/zram-control/hot_add"); ++ if (n < 0) ++ return n; ++ ++ DBG(fprintf(stderr, "hot-add: %d", n)); ++ zram_set_devname(z, NULL, n); ++ return 0; ++} ++ ++static int zram_control_remove(struct zram *z) ++{ ++ char str[sizeof stringify_value(INT_MAX)]; ++ int n; ++ ++ if (!zram_has_control(z)) ++ return -ENOSYS; ++ ++ n = zram_get_devnum(z); ++ if (n < 0) ++ return n; ++ ++ DBG(fprintf(stderr, "hot-remove: %d", n)); ++ snprintf(str, sizeof(str), "%d", n); ++ return path_write_str(str, _PATH_SYS_CLASS "/zram-control/hot_remove"); ++} ++ ++static struct zram *find_free_zram(void) ++{ ++ struct zram *z = new_zram(NULL); ++ size_t i; ++ int isfree = 0; ++ ++ for (i = 0; isfree == 0; i++) { ++ DBG(fprintf(stderr, "find free: checking zram%zu", i)); ++ zram_set_devname(z, NULL, i); ++ if (!zram_exist(z) && zram_control_add(z) != 0) ++ break; ++ isfree = !zram_used(z); ++ } ++ if (!isfree) { ++ free_zram(z); ++ z = NULL; ++ } ++ return z; ++} ++ ++static char *get_mm_stat(struct zram *z, size_t idx, int bytes) ++{ ++ struct sysfs_cxt *sysfs; ++ const char *name; ++ uint64_t num; ++ ++ assert(idx < ARRAY_SIZE(mm_stat_names)); ++ assert(z); ++ ++ sysfs = zram_get_sysfs(z); ++ if (!sysfs) ++ return NULL; ++ ++ /* Linux >= 4.1 uses /sys/block/zram<id>/mm_stat */ ++ if (!z->mm_stat && !z->mm_stat_probed) { ++ char *str; ++ ++ str = sysfs_strdup(sysfs, "mm_stat"); ++ if (str) { ++ z->mm_stat = strv_split(str, " "); ++ if (strv_length(z->mm_stat) < ARRAY_SIZE(mm_stat_names)) ++ errx(EXIT_FAILURE, _("Failed to parse mm_stat")); ++ } ++ z->mm_stat_probed = 1; ++ free(str); ++ ++ } ++ ++ if (z->mm_stat) { ++ if (bytes) ++ return xstrdup(z->mm_stat[idx]); ++ ++ num = strtou64_or_err(z->mm_stat[idx], _("Failed to parse mm_stat")); ++ return size_to_human_string(SIZE_SUFFIX_1LETTER, num); ++ } ++ ++ /* Linux < 4.1 uses /sys/block/zram<id>/<attrname> */ ++ name = mm_stat_names[idx]; ++ if (bytes) ++ return sysfs_strdup(sysfs, name); ++ else if (sysfs_read_u64(sysfs, name, &num) == 0) ++ return size_to_human_string(SIZE_SUFFIX_1LETTER, num); ++ return NULL; ++} ++ ++static void fill_table_row(struct libscols_table *tb, struct zram *z) ++{ ++ static struct libscols_line *ln; ++ struct sysfs_cxt *sysfs; ++ size_t i; ++ uint64_t num; ++ ++ assert(tb); ++ assert(z); ++ ++ DBG(fprintf(stderr, "%s: filling status table", z->devname)); ++ ++ sysfs = zram_get_sysfs(z); ++ if (!sysfs) ++ return; ++ ++ ln = scols_table_new_line(tb, NULL); ++ if (!ln) ++ err(EXIT_FAILURE, _("failed to initialize output line")); ++ ++ for (i = 0; i < (size_t) ncolumns; i++) { ++ char *str = NULL; ++ ++ switch (get_column_id(i)) { ++ case COL_NAME: ++ str = xstrdup(z->devname); ++ break; ++ case COL_DISKSIZE: ++ if (inbytes) ++ str = sysfs_strdup(sysfs, "disksize"); ++ else if (sysfs_read_u64(sysfs, "disksize", &num) == 0) ++ str = size_to_human_string(SIZE_SUFFIX_1LETTER, num); ++ break; ++ case COL_ALGORITHM: ++ { ++ char *alg = sysfs_strdup(sysfs, "comp_algorithm"); ++ if (!alg) ++ break; ++ if (strstr(alg, "[lzo]") == NULL) { ++ if (strstr(alg, "[lz4]") == NULL) ++ ; ++ else ++ str = xstrdup("lz4"); ++ } else ++ str = xstrdup("lzo"); ++ free(alg); ++ break; ++ } ++ case COL_MOUNTPOINT: ++ { ++ char path[PATH_MAX] = { '\0' }; ++ int fl; ++ ++ check_mount_point(z->devname, &fl, path, sizeof(path)); ++ if (*path) ++ str = xstrdup(path); ++ break; ++ } ++ case COL_STREAMS: ++ str = sysfs_strdup(sysfs, "max_comp_streams"); ++ break; ++ case COL_ZEROPAGES: ++ str = get_mm_stat(z, MM_ZERO_PAGES, 1); ++ break; ++ case COL_ORIG_SIZE: ++ str = get_mm_stat(z, MM_ORIG_DATA_SIZE, inbytes); ++ break; ++ case COL_COMP_SIZE: ++ str = get_mm_stat(z, MM_COMPR_DATA_SIZE, inbytes); ++ break; ++ case COL_MEMTOTAL: ++ str = get_mm_stat(z, MM_MEM_USED_TOTAL, inbytes); ++ break; ++ case COL_MEMLIMIT: ++ str = get_mm_stat(z, MM_MEM_LIMIT, inbytes); ++ break; ++ case COL_MEMUSED: ++ str = get_mm_stat(z, MM_MEM_USED_MAX, inbytes); ++ break; ++ case COL_MIGRATED: ++ str = get_mm_stat(z, MM_NUM_MIGRATED, inbytes); ++ break; ++ } ++ if (str) ++ scols_line_refer_data(ln, i, str); ++ } ++} ++ ++static void status(struct zram *z) ++{ ++ struct libscols_table *tb; ++ size_t i; ++ ++ scols_init_debug(0); ++ ++ tb = scols_new_table(); ++ if (!tb) ++ err(EXIT_FAILURE, _("failed to initialize output table")); ++ ++ scols_table_enable_raw(tb, raw); ++ scols_table_enable_noheadings(tb, no_headings); ++ ++ for (i = 0; i < (size_t) ncolumns; i++) { ++ const struct colinfo *col = get_column_info(i); ++ ++ if (!scols_table_new_column(tb, col->name, col->whint, col->flags)) ++ err(EXIT_FAILURE, _("failed to initialize output column")); ++ } ++ ++ if (z) ++ fill_table_row(tb, z); /* just one device specified */ ++ else { ++ /* list all used devices */ ++ z = new_zram(NULL); ++ ++ for (i = 0; ; i++) { ++ zram_set_devname(z, NULL, i); ++ if (!zram_exist(z)) ++ break; ++ if (zram_used(z)) ++ fill_table_row(tb, z); ++ } ++ free_zram(z); ++ } ++ ++ scols_print_table(tb); ++ scols_unref_table(tb); ++} ++ ++static void __attribute__ ((__noreturn__)) usage(FILE * out) ++{ ++ size_t i; ++ ++ fputs(USAGE_HEADER, out); ++ fprintf(out, _( " %1$s [options] <device>\n" ++ " %1$s -r <device> [...]\n" ++ " %1$s [options] -f | <device> -s <size>\n"), ++ program_invocation_short_name); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Set up and control zram devices.\n"), out); ++ ++ fputs(USAGE_OPTIONS, out); ++ fputs(_(" -a, --algorithm lzo|lz4 compression algorithm to use\n"), out); ++ fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out); ++ fputs(_(" -f, --find find a free device\n"), out); ++ fputs(_(" -n, --noheadings don't print headings\n"), out); ++ fputs(_(" -o, --output <list> columns to use for status output\n"), out); ++ fputs(_(" --raw use raw status output format\n"), out); ++ fputs(_(" -r, --reset reset all specified devices\n"), out); ++ fputs(_(" -s, --size <size> device size\n"), out); ++ fputs(_(" -t, --streams <number> number of compression streams\n"), out); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(USAGE_HELP, out); ++ fputs(USAGE_VERSION, out); ++ ++ fputs(_("\nAvailable columns (for --output):\n"), out); ++ for (i = 0; i < ARRAY_SIZE(infos); i++) ++ fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help)); ++ ++ fprintf(out, USAGE_MAN_TAIL("zramctl(8)")); ++ exit(out == stderr ? 1 : EXIT_SUCCESS); ++} ++ ++/* actions */ ++enum { ++ A_NONE = 0, ++ A_STATUS, ++ A_CREATE, ++ A_FINDONLY, ++ A_RESET ++}; ++ ++int main(int argc, char **argv) ++{ ++ uintmax_t size = 0, nstreams = 0; ++ char *algorithm = NULL; ++ int rc = 0, c, find = 0, act = A_NONE; ++ struct zram *zram = NULL; ++ ++ enum { OPT_RAW = CHAR_MAX + 1 }; ++ ++ static const struct option longopts[] = { ++ { "algorithm", required_argument, NULL, 'a' }, ++ { "bytes", no_argument, NULL, 'b' }, ++ { "find", no_argument, NULL, 'f' }, ++ { "help", no_argument, NULL, 'h' }, ++ { "output", required_argument, NULL, 'o' }, ++ { "noheadings",no_argument, NULL, 'n' }, ++ { "reset", no_argument, NULL, 'r' }, ++ { "raw", no_argument, NULL, OPT_RAW }, ++ { "size", required_argument, NULL, 's' }, ++ { "streams", required_argument, NULL, 't' }, ++ { "version", no_argument, NULL, 'V' }, ++ { NULL, 0, NULL, 0 } ++ }; ++ ++ static const ul_excl_t excl[] = { ++ { 'f', 'o', 'r' }, ++ { 'o', 'r', 's' }, ++ { 0 } ++ }; ++ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; ++ ++ setlocale(LC_ALL, ""); ++ bindtextdomain(PACKAGE, LOCALEDIR); ++ textdomain(PACKAGE); ++ atexit(close_stdout); ++ ++ while ((c = getopt_long(argc, argv, "a:bfho:nrs:t:V", longopts, NULL)) != -1) { ++ ++ err_exclusive_options(c, longopts, excl, excl_st); ++ ++ switch (c) { ++ case 'a': ++ if (strcmp(optarg,"lzo") && strcmp(optarg,"lz4")) ++ errx(EXIT_FAILURE, _("unsupported algorithm: %s"), ++ optarg); ++ algorithm = optarg; ++ break; ++ case 'b': ++ inbytes = 1; ++ break; ++ case 'f': ++ find = 1; ++ break; ++ case 'o': ++ ncolumns = string_to_idarray(optarg, ++ columns, ARRAY_SIZE(columns), ++ column_name_to_id); ++ if (ncolumns < 0) ++ return EXIT_FAILURE; ++ break; ++ case 's': ++ size = strtosize_or_err(optarg, _("failed to parse size")); ++ act = A_CREATE; ++ break; ++ case 't': ++ nstreams = strtou64_or_err(optarg, _("failed to parse streams")); ++ break; ++ case 'r': ++ act = A_RESET; ++ break; ++ case OPT_RAW: ++ raw = 1; ++ break; ++ case 'n': ++ no_headings = 1; ++ break; ++ case 'V': ++ printf(UTIL_LINUX_VERSION); ++ return EXIT_SUCCESS; ++ case 'h': ++ usage(stdout); ++ default: ++ usage(stderr); ++ } ++ } ++ ++ if (find && optind < argc) ++ errx(EXIT_FAILURE, _("option --find is mutually exclusive " ++ "with <device>")); ++ if (act == A_NONE) ++ act = find ? A_FINDONLY : A_STATUS; ++ ++ if (act != A_RESET && optind + 1 < argc) ++ errx(EXIT_FAILURE, _("only one <device> at a time is allowed")); ++ ++ if ((act == A_STATUS || act == A_FINDONLY) && (algorithm || nstreams)) ++ errx(EXIT_FAILURE, _("options --algorithm and --streams " ++ "must be combined with --size")); ++ ++ switch (act) { ++ case A_STATUS: ++ if (!ncolumns) { /* default columns */ ++ columns[ncolumns++] = COL_NAME; ++ columns[ncolumns++] = COL_ALGORITHM; ++ columns[ncolumns++] = COL_DISKSIZE; ++ columns[ncolumns++] = COL_ORIG_SIZE; ++ columns[ncolumns++] = COL_COMP_SIZE; ++ columns[ncolumns++] = COL_MEMTOTAL; ++ columns[ncolumns++] = COL_STREAMS; ++ columns[ncolumns++] = COL_MOUNTPOINT; ++ } ++ if (optind < argc) { ++ zram = new_zram(argv[optind++]); ++ if (!zram_exist(zram)) ++ err(EXIT_FAILURE, "%s", zram->devname); ++ } ++ status(zram); ++ free_zram(zram); ++ break; ++ case A_RESET: ++ if (optind == argc) ++ errx(EXIT_FAILURE, _("no device specified")); ++ while (optind < argc) { ++ zram = new_zram(argv[optind]); ++ if (!zram_exist(zram) ++ || zram_set_u64parm(zram, "reset", 1)) { ++ warn(_("%s: failed to reset"), zram->devname); ++ rc = 1; ++ } ++ zram_control_remove(zram); ++ free_zram(zram); ++ optind++; ++ } ++ break; ++ case A_FINDONLY: ++ zram = find_free_zram(); ++ if (!zram) ++ errx(EXIT_FAILURE, _("no free zram device found")); ++ printf("%s\n", zram->devname); ++ free_zram(zram); ++ break; ++ case A_CREATE: ++ if (find) { ++ zram = find_free_zram(); ++ if (!zram) ++ errx(EXIT_FAILURE, _("no free zram device found")); ++ } else if (optind == argc) ++ errx(EXIT_FAILURE, _("no device specified")); ++ else { ++ zram = new_zram(argv[optind]); ++ if (!zram_exist(zram)) ++ err(EXIT_FAILURE, "%s", zram->devname); ++ } ++ ++ if (zram_set_u64parm(zram, "reset", 1)) ++ err(EXIT_FAILURE, _("%s: failed to reset"), zram->devname); ++ ++ if (nstreams && ++ zram_set_u64parm(zram, "max_comp_streams", nstreams)) ++ err(EXIT_FAILURE, _("%s: failed to set number of streams"), zram->devname); ++ ++ if (algorithm && ++ zram_set_strparm(zram, "comp_algorithm", algorithm)) ++ err(EXIT_FAILURE, _("%s: failed to set algorithm"), zram->devname); ++ ++ if (zram_set_u64parm(zram, "disksize", size)) ++ err(EXIT_FAILURE, _("%s: failed to set disksize (%ju bytes)"), ++ zram->devname, size); ++ if (find) ++ printf("%s\n", zram->devname); ++ free_zram(zram); ++ break; ++ } ++ ++ return rc ? EXIT_FAILURE : EXIT_SUCCESS; ++} +-- +2.9.3 + diff --git a/SOURCES/0107-libblkid-zfs-let-s-keep-compiler-happy.patch b/SOURCES/0107-libblkid-zfs-let-s-keep-compiler-happy.patch new file mode 100644 index 0000000..ca625de --- /dev/null +++ b/SOURCES/0107-libblkid-zfs-let-s-keep-compiler-happy.patch @@ -0,0 +1,28 @@ +From eda050c1fc9f65fc138c9fabf35ef7865c5b10a7 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 30 May 2014 11:27:41 +0200 +Subject: [PATCH 107/116] libblkid: (zfs) let's keep compiler happy + +Upstream: http://github.com/karelzak/util-linux/commit/59cbbd71cdfb6a6fc6901e287b300aa8d45d6606 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1392661 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/superblocks/zfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c +index 56ee472..86da59d 100644 +--- a/libblkid/src/superblocks/zfs.c ++++ b/libblkid/src/superblocks/zfs.c +@@ -171,7 +171,7 @@ static int probe_zfs(blkid_probe pr, + uint64_t swab_magic = swab64(UBERBLOCK_MAGIC); + struct zfs_uberblock *ub; + int swab_endian; +- loff_t offset, ub_offset; ++ loff_t offset, ub_offset = 0; + int tried; + int found; + +-- +2.9.3 + diff --git a/SOURCES/0108-blkid-make-zfs-detection-more-robust.patch b/SOURCES/0108-blkid-make-zfs-detection-more-robust.patch new file mode 100644 index 0000000..7d929bd --- /dev/null +++ b/SOURCES/0108-blkid-make-zfs-detection-more-robust.patch @@ -0,0 +1,112 @@ +From a0e00751052b06508f6cfe4a434ebf2e1a04bf07 Mon Sep 17 00:00:00 2001 +From: Michal Humpula <michal.humpula@hudrydum.cz> +Date: Wed, 25 Feb 2015 20:25:05 +0100 +Subject: [PATCH 108/116] blkid: make zfs detection more robust + +Try to use all the possible uberblock locations. + +Upstream: https://github.com/karelzak/util-linux/commit/5dd705ba65dc80cf8630b3b1b4f3a1ba153e7eec +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1392661 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/superblocks/zfs.c | 42 +++++++++++++++++++++++++++--------------- + 1 file changed, 27 insertions(+), 15 deletions(-) + +diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c +index 86da59d..4a64a03 100644 +--- a/libblkid/src/superblocks/zfs.c ++++ b/libblkid/src/superblocks/zfs.c +@@ -19,6 +19,7 @@ + #define VDEV_LABEL_UBERBLOCK (128 * 1024ULL) + #define VDEV_LABEL_NVPAIR ( 16 * 1024ULL) + #define VDEV_LABEL_SIZE (256 * 1024ULL) ++#define UBERBLOCK_SIZE 1024ULL + + /* #include <sys/uberblock_impl.h> */ + #define UBERBLOCK_MAGIC 0x00bab10c /* oo-ba-bloc! */ +@@ -31,7 +32,7 @@ struct zfs_uberblock { + char ub_rootbp; /* MOS objset_phys_t */ + } __attribute__((packed)); + +-#define ZFS_TRIES 64 ++#define ZFS_TRIES 512 + #define ZFS_WANT 4 + + #define DATA_TYPE_UINT64 8 +@@ -162,11 +163,10 @@ static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) + #define zdebug(fmt, ...) do {} while(0) + /*#define zdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/ + +-/* ZFS has 128x1kB host-endian root blocks, stored in 2 areas at the start +- * of the disk, and 2 areas at the end of the disk. Check only some of them... +- * #4 (@ 132kB) is the first one written on a new filesystem. */ +-static int probe_zfs(blkid_probe pr, +- const struct blkid_idmag *mag __attribute__((__unused__))) ++/* ZFS has 128x1kB host-endian root blocks, stored in 2 areas (labels) ++ * at the start of the disk, and 2 areas at the end of the disk. ++ */ ++static int probe_zfs(blkid_probe pr, const struct blkid_idmag *mag) + { + uint64_t swab_magic = swab64(UBERBLOCK_MAGIC); + struct zfs_uberblock *ub; +@@ -174,15 +174,29 @@ static int probe_zfs(blkid_probe pr, + loff_t offset, ub_offset = 0; + int tried; + int found; ++ loff_t blk_align = (pr->size % (256 * 1024ULL)); + + zdebug("probe_zfs\n"); +- /* Look for at least 4 uberblocks to ensure a positive match */ ++ /* Look for at least 4 uberblocks to ensure a positive match. ++ Begin with Label 0 (L0) at the start of the block device. */ + for (tried = found = 0, offset = VDEV_LABEL_UBERBLOCK; +- tried < ZFS_TRIES && found < ZFS_WANT; +- tried++, offset += 4096) { +- /* also try the second uberblock copy */ +- if (tried == (ZFS_TRIES / 2)) ++ found < ZFS_WANT && tried < ZFS_TRIES; ++ tried++, offset += UBERBLOCK_SIZE) ++ { ++ /* Leave L0 to try other labels */ ++ switch(tried) { ++ case 128: // jump to L1, just after L0 + offset = VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK; ++ break; ++ case 256: // jump to L2 near the far end of the block device ++ offset = pr->size - 2 * VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK - blk_align; ++ zdebug("probe_zfs: l2 offset %llu\n", offset >> 10); ++ break; ++ case 384: // jump to L3 at the furthest end of the block device ++ offset = pr->size - VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK - blk_align; ++ zdebug("probe_zfs: l3 offset %llu\n", offset >> 10); ++ break; ++ } + + ub = (struct zfs_uberblock *) + blkid_probe_get_buffer(pr, offset, +@@ -193,15 +207,14 @@ static int probe_zfs(blkid_probe pr, + if (ub->ub_magic == UBERBLOCK_MAGIC) { + ub_offset = offset; + found++; ++ zdebug("probe_zfs: found little-endian uberblock at %llu\n", offset >> 10); + } + + if ((swab_endian = (ub->ub_magic == swab_magic))) { + ub_offset = offset; + found++; ++ zdebug("probe_zfs: found big-endian uberblock at %llu\n", offset >> 10); + } +- +- zdebug("probe_zfs: found %s-endian uberblock at %llu\n", +- swab_endian ? "big" : "little", offset >> 10); + } + + if (found < 4) +@@ -230,4 +243,3 @@ const struct blkid_idinfo zfs_idinfo = + .minsz = 64 * 1024 * 1024, + .magics = BLKID_NONE_MAGIC + }; +- +-- +2.9.3 + diff --git a/SOURCES/0109-zfs-make-less-syscalls.patch b/SOURCES/0109-zfs-make-less-syscalls.patch new file mode 100644 index 0000000..b43620d --- /dev/null +++ b/SOURCES/0109-zfs-make-less-syscalls.patch @@ -0,0 +1,154 @@ +From 8646f60d1b421b48b43405fa286548a0bf3f5da4 Mon Sep 17 00:00:00 2001 +From: Michal Humpula <michal.humpula@hudrydum.cz> +Date: Sat, 28 Feb 2015 21:19:42 +0100 +Subject: [PATCH 109/116] zfs: make less syscalls + +Upstream: https://github.com/karelzak/util-linux/commit/e44a4c7ac9522c03b76d8b62ce88b443771fdb0b +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1392661 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/superblocks/zfs.c | 95 ++++++++++++++++++++++++++---------------- + 1 file changed, 58 insertions(+), 37 deletions(-) + +diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c +index 4a64a03..9b5601e 100644 +--- a/libblkid/src/superblocks/zfs.c ++++ b/libblkid/src/superblocks/zfs.c +@@ -20,6 +20,7 @@ + #define VDEV_LABEL_NVPAIR ( 16 * 1024ULL) + #define VDEV_LABEL_SIZE (256 * 1024ULL) + #define UBERBLOCK_SIZE 1024ULL ++#define UBERBLOCKS_COUNT 128 + + /* #include <sys/uberblock_impl.h> */ + #define UBERBLOCK_MAGIC 0x00bab10c /* oo-ba-bloc! */ +@@ -32,7 +33,6 @@ struct zfs_uberblock { + char ub_rootbp; /* MOS objset_phys_t */ + } __attribute__((packed)); + +-#define ZFS_TRIES 512 + #define ZFS_WANT 4 + + #define DATA_TYPE_UINT64 8 +@@ -163,61 +163,82 @@ static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) + #define zdebug(fmt, ...) do {} while(0) + /*#define zdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/ + +-/* ZFS has 128x1kB host-endian root blocks, stored in 2 areas (labels) +- * at the start of the disk, and 2 areas at the end of the disk. +- */ ++static int find_uberblocks(const void *label, loff_t *ub_offset, int *swap_endian) ++{ ++ uint64_t swab_magic = swab64(UBERBLOCK_MAGIC); ++ struct zfs_uberblock *ub; ++ int i, found = 0; ++ loff_t offset = VDEV_LABEL_UBERBLOCK; ++ ++ for (i = 0; i < UBERBLOCKS_COUNT; i++, offset += UBERBLOCK_SIZE) { ++ ub = (struct zfs_uberblock *)(label + offset); ++ ++ if (ub->ub_magic == UBERBLOCK_MAGIC) { ++ *ub_offset = offset; ++ *swap_endian = 0; ++ found++; ++ zdebug("probe_zfs: found little-endian uberblock at %llu\n", offset >> 10); ++ } ++ ++ if (ub->ub_magic == swab_magic) { ++ *ub_offset = offset; ++ *swap_endian = 1; ++ found++; ++ zdebug("probe_zfs: found big-endian uberblock at %llu\n", offset >> 10); ++ } ++ } ++ ++ return found; ++} ++ ++/* ZFS has 128x1kB host-endian root blocks, stored in 2 areas at the start ++ * of the disk, and 2 areas at the end of the disk. Check only some of them... ++ * #4 (@ 132kB) is the first one written on a new filesystem. */ + static int probe_zfs(blkid_probe pr, const struct blkid_idmag *mag) + { + uint64_t swab_magic = swab64(UBERBLOCK_MAGIC); ++ int swab_endian = 0; + struct zfs_uberblock *ub; +- int swab_endian; + loff_t offset, ub_offset = 0; +- int tried; +- int found; ++ int label_no, found = 0, found_in_label; ++ void *label; + loff_t blk_align = (pr->size % (256 * 1024ULL)); + + zdebug("probe_zfs\n"); +- /* Look for at least 4 uberblocks to ensure a positive match. +- Begin with Label 0 (L0) at the start of the block device. */ +- for (tried = found = 0, offset = VDEV_LABEL_UBERBLOCK; +- found < ZFS_WANT && tried < ZFS_TRIES; +- tried++, offset += UBERBLOCK_SIZE) +- { +- /* Leave L0 to try other labels */ +- switch(tried) { +- case 128: // jump to L1, just after L0 +- offset = VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK; ++ /* Look for at least 4 uberblocks to ensure a positive match */ ++ for (label_no = 0; label_no < 4; label_no++) { ++ switch(label_no) { ++ case 0: // jump to L0 ++ offset = 0; + break; +- case 256: // jump to L2 near the far end of the block device +- offset = pr->size - 2 * VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK - blk_align; +- zdebug("probe_zfs: l2 offset %llu\n", offset >> 10); ++ case 1: // jump to L1 ++ offset = VDEV_LABEL_SIZE; + break; +- case 384: // jump to L3 at the furthest end of the block device +- offset = pr->size - VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK - blk_align; +- zdebug("probe_zfs: l3 offset %llu\n", offset >> 10); ++ case 2: // jump to L2 ++ offset = pr->size - 2 * VDEV_LABEL_SIZE - blk_align; ++ break; ++ case 3: // jump to L3 ++ offset = pr->size - VDEV_LABEL_SIZE - blk_align; + break; + } + +- ub = (struct zfs_uberblock *) +- blkid_probe_get_buffer(pr, offset, +- sizeof(struct zfs_uberblock)); +- if (ub == NULL) ++ label = blkid_probe_get_buffer(pr, offset, VDEV_LABEL_SIZE); ++ if (label == NULL) + return errno ? -errno : 1; + +- if (ub->ub_magic == UBERBLOCK_MAGIC) { +- ub_offset = offset; +- found++; +- zdebug("probe_zfs: found little-endian uberblock at %llu\n", offset >> 10); +- } ++ found_in_label = find_uberblocks(label, &ub_offset, &swab_endian); + +- if ((swab_endian = (ub->ub_magic == swab_magic))) { +- ub_offset = offset; +- found++; +- zdebug("probe_zfs: found big-endian uberblock at %llu\n", offset >> 10); ++ if (found_in_label > 0) { ++ found+= found_in_label; ++ ub = (struct zfs_uberblock *)(label + ub_offset); ++ ub_offset += offset; ++ ++ if (found >= ZFS_WANT) ++ break; + } + } + +- if (found < 4) ++ if (found < ZFS_WANT) + return 1; + + /* If we found the 4th uberblock, then we will have exited from the +-- +2.9.3 + diff --git a/SOURCES/0110-libblkid-zfs-keep-bufferes-read-only.patch b/SOURCES/0110-libblkid-zfs-keep-bufferes-read-only.patch new file mode 100644 index 0000000..ad08711 --- /dev/null +++ b/SOURCES/0110-libblkid-zfs-keep-bufferes-read-only.patch @@ -0,0 +1,48 @@ +From eeb629102da9a9a916d201bbd5f13ee7ad23844d Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 22 Sep 2015 15:27:39 +0200 +Subject: [PATCH 110/116] libblkid: (zfs) keep bufferes read-only + +Upstream: https://github.com/karelzak/util-linux/commit/9325a8be9e55cc01d9f580e14dd2193829245183 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1392661 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/superblocks/zfs.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c +index 9b5601e..8e88b39 100644 +--- a/libblkid/src/superblocks/zfs.c ++++ b/libblkid/src/superblocks/zfs.c +@@ -70,9 +70,10 @@ struct nvlist { + + static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) + { ++ unsigned char *p, buff[4096]; + struct nvlist *nvl; + struct nvpair *nvp; +- size_t left = 4096; ++ size_t left = sizeof(buff); + int found = 0; + + offset = (offset & ~(VDEV_LABEL_SIZE - 1)) + VDEV_LABEL_NVPAIR; +@@ -81,10 +82,14 @@ static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) + * the first 4k (left) of the nvlist. This is true for all pools + * I've seen, and simplifies this code somewhat, because we don't + * have to handle an nvpair crossing a buffer boundary. */ +- nvl = (struct nvlist *)blkid_probe_get_buffer(pr, offset, left); +- if (nvl == NULL) ++ p = blkid_probe_get_buffer(pr, offset, left); ++ if (!p) + return; + ++ /* libblkid buffers are strictly readonly, but the code below modifies nvpair etc. */ ++ memcpy(buff, p, sizeof(buff)); ++ nvl = (struct nvlist *) buff; ++ + nvdebug("zfs_extract: nvlist offset %llu\n", offset); + + nvp = &nvl->nvl_nvpair; +-- +2.9.3 + diff --git a/SOURCES/0111-libblkid-don-t-mark-zfs-as-RAID.patch b/SOURCES/0111-libblkid-don-t-mark-zfs-as-RAID.patch new file mode 100644 index 0000000..8436e87 --- /dev/null +++ b/SOURCES/0111-libblkid-don-t-mark-zfs-as-RAID.patch @@ -0,0 +1,28 @@ +From 400facb19f228e3507de2137e2f9165b3365b7df Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 18 Nov 2015 11:55:35 +0100 +Subject: [PATCH 111/116] libblkid: don't mark zfs as RAID + +Upstream: https://github.com/karelzak/util-linux/commit/710dd492394be6cd2f3dd65c057ae7827ee6e89d +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1392661 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/superblocks/zfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c +index 8e88b39..5074495 100644 +--- a/libblkid/src/superblocks/zfs.c ++++ b/libblkid/src/superblocks/zfs.c +@@ -264,7 +264,7 @@ static int probe_zfs(blkid_probe pr, const struct blkid_idmag *mag) + const struct blkid_idinfo zfs_idinfo = + { + .name = "zfs_member", +- .usage = BLKID_USAGE_RAID, ++ .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_zfs, + .minsz = 64 * 1024 * 1024, + .magics = BLKID_NONE_MAGIC +-- +2.9.3 + diff --git a/SOURCES/0112-tests-update-ZFS-test.patch b/SOURCES/0112-tests-update-ZFS-test.patch new file mode 100644 index 0000000..9368222 --- /dev/null +++ b/SOURCES/0112-tests-update-ZFS-test.patch @@ -0,0 +1,28 @@ +From 0f0a36e64ed47721e8b8d41b81ae3b1817d19194 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 19 Nov 2015 19:08:00 +0100 +Subject: [PATCH 112/116] tests: update ZFS test + +Upstream: https://github.com/karelzak/util-linux/commit/0172ebba52718afe89b5d1941266825b065358cb +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1392661 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + tests/expected/blkid/low-probe-zfs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/expected/blkid/low-probe-zfs b/tests/expected/blkid/low-probe-zfs +index 46727de..952e0e5 100644 +--- a/tests/expected/blkid/low-probe-zfs ++++ b/tests/expected/blkid/low-probe-zfs +@@ -1,7 +1,7 @@ + ID_FS_LABEL=tank + ID_FS_LABEL_ENC=tank + ID_FS_TYPE=zfs_member +-ID_FS_USAGE=raid ++ID_FS_USAGE=filesystem + ID_FS_UUID=1782036546311300980 + ID_FS_UUID_ENC=1782036546311300980 + ID_FS_UUID_SUB=13179280127379850514 +-- +2.9.3 + diff --git a/SOURCES/0113-libblkid-zfs-add-cast-to-fix-UB-cppcheck.patch b/SOURCES/0113-libblkid-zfs-add-cast-to-fix-UB-cppcheck.patch new file mode 100644 index 0000000..6ac7f51 --- /dev/null +++ b/SOURCES/0113-libblkid-zfs-add-cast-to-fix-UB-cppcheck.patch @@ -0,0 +1,31 @@ +From a569050329d914ad5aa15978f2a4a3d969c7c8b1 Mon Sep 17 00:00:00 2001 +From: Boris Egorov <egorov@linux.com> +Date: Tue, 19 Jan 2016 11:37:57 +0600 +Subject: [PATCH 113/116] libblkid: (zfs) add cast to fix UB [cppcheck] + +[libblkid/src/superblocks/zfs.c:173]: (error) Shifting 32-bit value by 56 bits is undefined behaviour +[libblkid/src/superblocks/zfs.c:173]: (error) Shifting 32-bit value by 40 bits is undefined behaviour + +Upstream: https://github.com/karelzak/util-linux/commit/18b76be61c11fd5f11fcb84aa6a946d3b03d7225 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1392661 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/superblocks/zfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c +index 5074495..ff12fa6 100644 +--- a/libblkid/src/superblocks/zfs.c ++++ b/libblkid/src/superblocks/zfs.c +@@ -170,7 +170,7 @@ static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) + + static int find_uberblocks(const void *label, loff_t *ub_offset, int *swap_endian) + { +- uint64_t swab_magic = swab64(UBERBLOCK_MAGIC); ++ uint64_t swab_magic = swab64((uint64_t)UBERBLOCK_MAGIC); + struct zfs_uberblock *ub; + int i, found = 0; + loff_t offset = VDEV_LABEL_UBERBLOCK; +-- +2.9.3 + diff --git a/SOURCES/0114-libblkid-Avoid-OOB-access-on-illegal-ZFS-superblocks.patch b/SOURCES/0114-libblkid-Avoid-OOB-access-on-illegal-ZFS-superblocks.patch new file mode 100644 index 0000000..9d58bd6 --- /dev/null +++ b/SOURCES/0114-libblkid-Avoid-OOB-access-on-illegal-ZFS-superblocks.patch @@ -0,0 +1,48 @@ +From 7e1c9da4773237e368bdc0539ef91d55ef19806c Mon Sep 17 00:00:00 2001 +From: Tobias Stoeckmann <tobias@stoeckmann.org> +Date: Sun, 28 Aug 2016 21:15:59 +0200 +Subject: [PATCH 114/116] libblkid: Avoid OOB access on illegal ZFS superblocks + +64 bit systems can trigger an out of boundary access while performing +a ZFS superblock probe. + +This happens due to a possible integer overflow while calculating +the remaining available bytes. The variable is of type "int" and the +string length is allowed to be larger than INT_MAX, which means that +avail calculation can overflow, circumventing the "avail < 0" check and +therefore accessing memory outside the "buff" array later on. + +[kzak@redhat.com (rhel7): - remove unused swab_magic] + +Upstream: https://github.com/karelzak/util-linux/commit/8fa57ab0b5696031da800e243def32bc5265ff6d +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1392661 +Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/superblocks/zfs.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c +index ff12fa6..2c7b4b7 100644 +--- a/libblkid/src/superblocks/zfs.c ++++ b/libblkid/src/superblocks/zfs.c +@@ -115,7 +115,7 @@ static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) + + nvs->nvs_type = be32_to_cpu(nvs->nvs_type); + nvs->nvs_strlen = be32_to_cpu(nvs->nvs_strlen); +- if (nvs->nvs_strlen > UINT_MAX - sizeof(*nvs)) ++ if (nvs->nvs_strlen > INT_MAX - sizeof(*nvs)) + break; + avail -= nvs->nvs_strlen + sizeof(*nvs); + nvdebug("nvstring: type %u string %*s\n", nvs->nvs_type, +@@ -201,7 +201,6 @@ static int find_uberblocks(const void *label, loff_t *ub_offset, int *swap_endia + * #4 (@ 132kB) is the first one written on a new filesystem. */ + static int probe_zfs(blkid_probe pr, const struct blkid_idmag *mag) + { +- uint64_t swab_magic = swab64(UBERBLOCK_MAGIC); + int swab_endian = 0; + struct zfs_uberblock *ub; + loff_t offset, ub_offset = 0; +-- +2.9.3 + diff --git a/SOURCES/0115-lscpu-backport-from-v2.29.patch b/SOURCES/0115-lscpu-backport-from-v2.29.patch new file mode 100644 index 0000000..9d95342 --- /dev/null +++ b/SOURCES/0115-lscpu-backport-from-v2.29.patch @@ -0,0 +1,2190 @@ +From 7ffb3c628dea313496c829bcb40447545470847e Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 21 Mar 2017 14:57:37 +0100 +Subject: [PATCH 115/116] lscpu: backport from v2.29 + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1360764 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1397709 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + configure.ac | 3 +- + include/c.h | 30 ++ + include/pathnames.h | 2 + + include/xalloc.h | 10 + + sys-utils/Makemodule.am | 8 +- + sys-utils/lscpu-dmi.c | 285 ++++++++++++++ + sys-utils/lscpu.1 | 53 ++- + sys-utils/lscpu.c | 964 ++++++++++++++++++++++++++++++++++++++++-------- + sys-utils/lscpu.h | 26 ++ + 9 files changed, 1220 insertions(+), 161 deletions(-) + create mode 100644 sys-utils/lscpu-dmi.c + create mode 100644 sys-utils/lscpu.h + +diff --git a/configure.ac b/configure.ac +index db7095a..78258d6 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1059,8 +1059,9 @@ AM_CONDITIONAL(BUILD_LSBLK, test "x$build_lsblk" = xyes) + + UL_BUILD_INIT([lscpu], [check]) + UL_REQUIRES_LINUX([lscpu]) ++UL_REQUIRES_BUILD([lscpu], [libsmartcols]) + UL_REQUIRES_HAVE([lscpu], [cpu_set_t], [cpu_set_t type]) +-AM_CONDITIONAL(BUILD_LSCPU, test "x$build_lscpu" = xyes) ++AM_CONDITIONAL([BUILD_LSCPU], [test "x$build_lscpu" = xyes]) + + + UL_BUILD_INIT([lslogins], [check]) +diff --git a/include/c.h b/include/c.h +index 3754e75..8ff61b4 100644 +--- a/include/c.h ++++ b/include/c.h +@@ -200,6 +200,19 @@ errmsg(char doexit, int excode, char adderr, const char *fmt, ...) + #endif + #endif /* !HAVE_ERR_H */ + ++/* Don't use inline function to avoid '#include "nls.h"' in c.h ++ */ ++#define errtryhelp(eval) __extension__ ({ \ ++ fprintf(stderr, _("Try '%s --help' for more information.\n"), \ ++ program_invocation_short_name); \ ++ exit(eval); \ ++}) ++ ++#define errtryh(eval) __extension__ ({ \ ++ fprintf(stderr, _("Try '%s -h' for more information.\n"), \ ++ program_invocation_short_name); \ ++ exit(eval); \ ++}) + + static inline __attribute__((const)) int is_power_of_2(unsigned long num) + { +@@ -317,6 +330,23 @@ static inline int usleep(useconds_t usec) + #define stringify(s) #s + + /* ++ * UL_ASAN_BLACKLIST is a macro to tell AddressSanitizer (a compile-time ++ * instrumentation shipped with Clang and GCC) to not instrument the ++ * annotated function. Furthermore, it will prevent the compiler from ++ * inlining the function because inlining currently breaks the blacklisting ++ * mechanism of AddressSanitizer. ++ */ ++#if defined(__has_feature) ++# if __has_feature(address_sanitizer) ++# define UL_ASAN_BLACKLIST __attribute__((noinline)) __attribute__((no_sanitize_memory)) __attribute__((no_sanitize_address)) ++# else ++# define UL_ASAN_BLACKLIST /* nothing */ ++# endif ++#else ++# define UL_ASAN_BLACKLIST /* nothing */ ++#endif ++ ++/* + * Note that sysconf(_SC_GETPW_R_SIZE_MAX) returns *initial* suggested size for + * pwd buffer and in some cases it is not large enough. See POSIX and + * getpwnam_r man page for more details. +diff --git a/include/pathnames.h b/include/pathnames.h +index b648afc..fa4bddb 100644 +--- a/include/pathnames.h ++++ b/include/pathnames.h +@@ -131,6 +131,8 @@ + # define _PATH_DEV "/dev/" + #endif + ++#define _PATH_DEV_MEM "/dev/mem" ++ + #define _PATH_DEV_LOOP "/dev/loop" + #define _PATH_DEV_LOOPCTL "/dev/loop-control" + #define _PATH_DEV_TTY "/dev/tty" +diff --git a/include/xalloc.h b/include/xalloc.h +index 1a1799a..883e472 100644 +--- a/include/xalloc.h ++++ b/include/xalloc.h +@@ -99,6 +99,16 @@ static inline int __attribute__ ((__format__(printf, 2, 3))) + } + + ++static inline int __attribute__ ((__format__(printf, 2, 0))) ++xvasprintf(char **strp, const char *fmt, va_list ap) ++{ ++ int ret = vasprintf(&(*strp), fmt, ap); ++ if (ret < 0) ++ err(XALLOC_EXIT_CODE, "cannot allocate string"); ++ return ret; ++} ++ ++ + static inline char *xgethostname(void) + { + char *name; +diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am +index 408e884..0496b84 100644 +--- a/sys-utils/Makemodule.am ++++ b/sys-utils/Makemodule.am +@@ -274,8 +274,12 @@ endif + + if BUILD_LSCPU + usrbin_exec_PROGRAMS += lscpu +-lscpu_SOURCES = sys-utils/lscpu.c +-lscpu_LDADD = $(LDADD) libcommon.la ++lscpu_SOURCES = \ ++ sys-utils/lscpu.c \ ++ sys-utils/lscpu.h \ ++ sys-utils/lscpu-dmi.c ++lscpu_LDADD = $(LDADD) libcommon.la libsmartcols.la $(RTAS_LIBS) ++lscpu_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) + dist_man_MANS += sys-utils/lscpu.1 + endif + +diff --git a/sys-utils/lscpu-dmi.c b/sys-utils/lscpu-dmi.c +new file mode 100644 +index 0000000..0e497d1 +--- /dev/null ++++ b/sys-utils/lscpu-dmi.c +@@ -0,0 +1,285 @@ ++/* ++ * lscpu-dmi - Module to parse SMBIOS information ++ * ++ * 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. ++ * ++ * Code originally taken from the dmidecode utility and slightly rewritten ++ * to suite the needs of lscpu ++ */ ++#include <errno.h> ++#include <stdlib.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <fcntl.h> ++#include <unistd.h> ++#include <string.h> ++#include <stdio.h> ++ ++#include "c.h" ++#include "pathnames.h" ++#include "all-io.h" ++#include "lscpu.h" ++ ++#define WORD(x) (uint16_t)(*(const uint16_t *)(x)) ++#define DWORD(x) (uint32_t)(*(const uint32_t *)(x)) ++ ++struct dmi_header ++{ ++ uint8_t type; ++ uint8_t length; ++ uint16_t handle; ++ uint8_t *data; ++}; ++ ++static int checksum(const uint8_t *buf, size_t len) ++{ ++ uint8_t sum = 0; ++ size_t a; ++ ++ for (a = 0; a < len; a++) ++ sum += buf[a]; ++ return (sum == 0); ++} ++ ++static void *get_mem_chunk(size_t base, size_t len, const char *devmem) ++{ ++ void *p = NULL; ++ int fd; ++ ++ if ((fd = open(devmem, O_RDONLY)) < 0) ++ return NULL; ++ ++ if (!(p = malloc(len))) ++ goto nothing; ++ if (lseek(fd, base, SEEK_SET) == -1) ++ goto nothing; ++ if (read_all(fd, p, len) == -1) ++ goto nothing; ++ ++ close(fd); ++ return p; ++ ++nothing: ++ free(p); ++ close(fd); ++ return NULL; ++} ++ ++static void to_dmi_header(struct dmi_header *h, uint8_t *data) ++{ ++ h->type = data[0]; ++ h->length = data[1]; ++ h->handle = WORD(data + 2); ++ h->data = data; ++} ++ ++static char *dmi_string(const struct dmi_header *dm, uint8_t s) ++{ ++ char *bp = (char *)dm->data; ++ ++ if (s == 0) ++ return NULL; ++ ++ bp += dm->length; ++ while (s > 1 && *bp) ++ { ++ bp += strlen(bp); ++ bp++; ++ s--; ++ } ++ ++ if (!*bp) ++ return NULL; ++ ++ return bp; ++} ++ ++static int hypervisor_from_dmi_table(uint32_t base, uint16_t len, ++ uint16_t num, const char *devmem) ++{ ++ uint8_t *buf; ++ uint8_t *data; ++ int i = 0; ++ char *vendor = NULL; ++ char *product = NULL; ++ char *manufacturer = NULL; ++ int rc = HYPER_NONE; ++ ++ data = buf = get_mem_chunk(base, len, devmem); ++ if (!buf) ++ goto done; ++ ++ /* 4 is the length of an SMBIOS structure header */ ++ while (i < num && data + 4 <= buf + len) { ++ uint8_t *next; ++ struct dmi_header h; ++ ++ to_dmi_header(&h, data); ++ ++ /* ++ * If a short entry is found (less than 4 bytes), not only it ++ * is invalid, but we cannot reliably locate the next entry. ++ * Better stop at this point. ++ */ ++ if (h.length < 4) ++ goto done; ++ ++ /* look for the next handle */ ++ next = data + h.length; ++ while (next - buf + 1 < len && (next[0] != 0 || next[1] != 0)) ++ next++; ++ next += 2; ++ switch (h.type) { ++ case 0: ++ vendor = dmi_string(&h, data[0x04]); ++ break; ++ case 1: ++ manufacturer = dmi_string(&h, data[0x04]); ++ product = dmi_string(&h, data[0x05]); ++ break; ++ default: ++ break; ++ } ++ ++ data = next; ++ i++; ++ } ++ if (manufacturer && !strcmp(manufacturer, "innotek GmbH")) ++ rc = HYPER_INNOTEK; ++ else if (manufacturer && strstr(manufacturer, "HITACHI") && ++ product && strstr(product, "LPAR")) ++ rc = HYPER_HITACHI; ++ else if (vendor && !strcmp(vendor, "Parallels")) ++ rc = HYPER_PARALLELS; ++done: ++ free(buf); ++ return rc; ++} ++ ++#if defined(__x86_64__) || defined(__i386__) ++static int hypervisor_decode_legacy(uint8_t *buf, const char *devmem) ++{ ++ if (!checksum(buf, 0x0F)) ++ return HYPER_NONE; ++ ++ return hypervisor_from_dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), ++ WORD(buf + 0x0C), ++ devmem); ++} ++#endif ++ ++static int hypervisor_decode_smbios(uint8_t *buf, const char *devmem) ++{ ++ if (!checksum(buf, buf[0x05]) ++ || memcmp(buf + 0x10, "_DMI_", 5) != 0 ++ || !checksum(buf + 0x10, 0x0F)) ++ return -1; ++ ++ return hypervisor_from_dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), ++ WORD(buf + 0x1C), ++ devmem); ++} ++ ++/* ++ * Probe for EFI interface ++ */ ++#define EFI_NOT_FOUND (-1) ++#define EFI_NO_SMBIOS (-2) ++static int address_from_efi(size_t *address) ++{ ++ FILE *tab; ++ char linebuf[64]; ++ int ret; ++ ++ *address = 0; /* Prevent compiler warning */ ++ ++ /* ++ * Linux up to 2.6.6: /proc/efi/systab ++ * Linux 2.6.7 and up: /sys/firmware/efi/systab ++ */ ++ if (!(tab = fopen("/sys/firmware/efi/systab", "r")) && ++ !(tab = fopen("/proc/efi/systab", "r"))) ++ return EFI_NOT_FOUND; /* No EFI interface */ ++ ++ ret = EFI_NO_SMBIOS; ++ while ((fgets(linebuf, sizeof(linebuf) - 1, tab)) != NULL) { ++ char *addrp = strchr(linebuf, '='); ++ if (!addrp) ++ continue; ++ *(addrp++) = '\0'; ++ if (strcmp(linebuf, "SMBIOS") == 0) { ++ *address = strtoul(addrp, NULL, 0); ++ ret = 0; ++ break; ++ } ++ } ++ ++ fclose(tab); ++ return ret; ++} ++ ++int read_hypervisor_dmi(void) ++{ ++ int rc = HYPER_NONE; ++ uint8_t *buf = NULL; ++ size_t fp = 0; ++ ++ if (sizeof(uint8_t) != 1 ++ || sizeof(uint16_t) != 2 ++ || sizeof(uint32_t) != 4 ++ || '\0' != 0) ++ return rc; ++ ++ /* First try EFI (ia64, Intel-based Mac) */ ++ switch (address_from_efi(&fp)) { ++ case EFI_NOT_FOUND: ++ goto memory_scan; ++ case EFI_NO_SMBIOS: ++ goto done; ++ } ++ ++ buf = get_mem_chunk(fp, 0x20, _PATH_DEV_MEM); ++ if (!buf) ++ goto done; ++ ++ rc = hypervisor_decode_smbios(buf, _PATH_DEV_MEM); ++ if (rc) ++ goto done; ++ free(buf); ++ buf = NULL; ++memory_scan: ++#if defined(__x86_64__) || defined(__i386__) ++ /* Fallback to memory scan (x86, x86_64) */ ++ buf = get_mem_chunk(0xF0000, 0x10000, _PATH_DEV_MEM); ++ if (!buf) ++ goto done; ++ ++ for (fp = 0; fp <= 0xFFF0; fp += 16) { ++ if (memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) { ++ rc = hypervisor_decode_smbios(buf + fp, _PATH_DEV_MEM); ++ if (rc == -1) ++ fp += 16; ++ ++ } else if (memcmp(buf + fp, "_DMI_", 5) == 0) ++ rc = hypervisor_decode_legacy(buf + fp, _PATH_DEV_MEM); ++ ++ if (rc >= 0) ++ break; ++ } ++#endif ++done: ++ free(buf); ++ return rc; ++} +diff --git a/sys-utils/lscpu.1 b/sys-utils/lscpu.1 +index f747a35..8636e52 100644 +--- a/sys-utils/lscpu.1 ++++ b/sys-utils/lscpu.1 +@@ -1,34 +1,42 @@ +-.\" Process this file with +-.\" groff -man -Tascii lscpu.1 +-.\" +-.TH LSCPU 1 "January 2013" "util-linux" "User Commands" ++.TH LSCPU 1 "November 2015" "util-linux" "User Commands" + .SH NAME + lscpu \- display information about the CPU architecture + .SH SYNOPSIS + .B lscpu +-.RB [ \-a | \-b | \-c "] [" \-x "] [" \-s " \fIdirectory\fP] [" \-e [=\fIlist\fP]| \-p [=\fIlist\fP]] ++.RB [ \-a | \-b | \-c | \-J "] [" \-x "] [" \-y "] [" \-s " \fIdirectory\fP] [" \-e [=\fIlist\fP]| \-p [=\fIlist\fP]] + .br + .B lscpu + .BR \-h | \-V + .SH DESCRIPTION + .B lscpu +-gathers CPU architecture information from sysfs and /proc/cpuinfo. The ++gathers CPU architecture information from sysfs, /proc/cpuinfo and any ++applicable architecture-specific libraries (e.g.\& librtas on Powerpc). The + command output can be optimized for parsing or for easy readability by humans. + The information includes, for example, the number of CPUs, threads, cores, + sockets, and Non-Uniform Memory Access (NUMA) nodes. There is also information + about the CPU caches and cache sharing, family, model, bogoMIPS, byte order, + and stepping. +- ++.sp ++In virtualized environments, the CPU architecture information displayed ++reflects the configuration of the guest operating system which is ++typically different from the physical (host) system. On architectures that ++support retrieving physical topology information, ++.B lscpu ++also displays the number of physical sockets, chips, cores in the host system. ++.sp + Options that result in an output table have a \fIlist\fP argument. Use this + argument to customize the command output. Specify a comma-separated list of + column labels to limit the output table to only the specified columns, arranged + in the specified order. See \fBCOLUMNS\fP for a list of valid column labels. The + column labels are not case sensitive. +- ++.sp + Not all columns are supported on all architectures. If an unsupported column is + specified, \fBlscpu\fP prints the column but does not provide any data for it. + + .SS COLUMNS ++Note that topology elements (core, socket, etc.) use a sequential unique ID ++starting from zero, but CPU logical numbers follow the kernel where there is ++no guarantee of sequential numbering. + .TP + .B CPU + The logical CPU number of a CPU as used by the Linux kernel. +@@ -42,8 +50,11 @@ The logical socket number. A socket can contain several cores. + .B BOOK + The logical book number. A book can contain several sockets. + .TP ++.B DRAWER ++The logical drawer number. A drawer can contain several books. ++.TP + .B NODE +-The logical NUMA node number. A node may contain several books. ++The logical NUMA node number. A node can contain several drawers. + .TP + .B CACHE + Information about how caches are shared between CPUs. +@@ -77,6 +88,14 @@ For vertical polarization, the column also shows the degree of concentration, + high, medium, or low. This column contains data only if your hardware system + and hypervisor support CPU polarization. + .RE ++.TP ++.B MAXMHZ ++Maximum megahertz value for the CPU. Useful when \fBlscpu\fP is used as hardware ++inventory information gathering tool. Notice that the megahertz value is ++dynamic, and driven by CPU governor depending on current resource need. ++.TP ++.B MINMHZ ++Minimum megahertz value for the CPU. + .SH OPTIONS + .TP + .BR \-a , " \-\-all" +@@ -92,7 +111,7 @@ Limit the output to offline CPUs. + This option may only be specified together with option \fB-e\fR or \fB-p\fR. + .TP + .BR \-e , " \-\-extended" [=\fIlist\fP] +-Display the CPU information in human readable format. ++Display the CPU information in human-readable format. + + If the \fIlist\fP argument is omitted, all columns for which data is available + are included in the command output. +@@ -102,7 +121,7 @@ When specifying the \fIlist\fP argument, the string of option, equal sign (=), a + Examples: '\fB-e=cpu,node\fP' or '\fB--extended=cpu,node\fP'. + .TP + .BR \-h , " \-\-help" +-Display help information and exit. ++Display help text and exit. + .TP + .BR \-p , " \-\-parse" [=\fIlist\fP] + Optimize the command output for easy parsing. +@@ -126,6 +145,16 @@ of the Linux instance to be inspected. + Use hexadecimal masks for CPU sets (for example 0x3). The default is to print + the sets in list format (for example 0,1). + .TP ++.BR \-y , " \-\-physical" ++Display physical IDs for all columns with topology elements (core, socket, etc.). ++Other than logical IDs, which are assigned by \fBlscpu\fP, physical IDs are ++platform-specific values that are provided by the kernel. Physical IDs are not ++necessarily unique and they might not be arranged sequentially. ++If the kernel could not retrieve a physical ID for an element \fBlscpu\fP prints ++the dash (-) character. ++ ++The CPU logical numbers are not affected by this option. ++.TP + .BR \-V , " \-\-version" + Display version information and exit. + .SH BUGS +@@ -145,4 +174,4 @@ Heiko Carstens <heiko.carstens@de.ibm.com> + .BR chcpu (8) + .SH AVAILABILITY + The lscpu command is part of the util-linux package and is available from +-ftp://ftp.kernel.org/pub/linux/utils/util-linux/. ++https://www.kernel.org/pub/linux/utils/util-linux/. +diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c +index 7a00636..683fd66 100644 +--- a/sys-utils/lscpu.c ++++ b/sys-utils/lscpu.c +@@ -19,6 +19,7 @@ + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + ++#include <assert.h> + #include <ctype.h> + #include <dirent.h> + #include <errno.h> +@@ -33,21 +34,46 @@ + #include <sys/types.h> + #include <sys/stat.h> + ++#if (defined(__x86_64__) || defined(__i386__)) ++# if !defined( __SANITIZE_ADDRESS__) ++# define INCLUDE_VMWARE_BDOOR ++# else ++# warning VMWARE detection disabled by __SANITIZE_ADDRESS__ ++# endif ++#endif ++ ++#ifdef INCLUDE_VMWARE_BDOOR ++# include <stdint.h> ++# include <signal.h> ++# include <strings.h> ++# include <setjmp.h> ++# ifdef HAVE_SYS_IO_H ++# include <sys/io.h> ++# endif ++#endif ++ ++#if defined(HAVE_LIBRTAS) ++#include <librtas.h> ++#endif ++ ++#include <libsmartcols.h> ++ + #include "cpuset.h" + #include "nls.h" + #include "xalloc.h" + #include "c.h" + #include "strutils.h" + #include "bitops.h" +-#include "tt.h" + #include "path.h" + #include "closestream.h" + #include "optutils.h" ++#include "lscpu.h" + + #define CACHE_MAX 100 + + /* /sys paths */ + #define _PATH_SYS_SYSTEM "/sys/devices/system" ++#define _PATH_SYS_HYP_FEATURES "/sys/hypervisor/properties/features" + #define _PATH_SYS_CPU _PATH_SYS_SYSTEM "/cpu" + #define _PATH_SYS_NODE _PATH_SYS_SYSTEM "/node" + #define _PATH_PROC_XEN "/proc/xen" +@@ -55,35 +81,71 @@ + #define _PATH_PROC_CPUINFO "/proc/cpuinfo" + #define _PATH_PROC_PCIDEVS "/proc/bus/pci/devices" + #define _PATH_PROC_SYSINFO "/proc/sysinfo" ++#define _PATH_PROC_STATUS "/proc/self/status" ++#define _PATH_PROC_VZ "/proc/vz" ++#define _PATH_PROC_BC "/proc/bc" ++#define _PATH_PROC_DEVICETREE "/proc/device-tree" ++#define _PATH_DEV_MEM "/dev/mem" ++#define _PATH_PROC_OSRELEASE "/proc/sys/kernel/osrelease" ++ ++/* Xen Domain feature flag used for /sys/hypervisor/properties/features */ ++#define XENFEAT_supervisor_mode_kernel 3 ++#define XENFEAT_mmu_pt_update_preserve_ad 5 ++#define XENFEAT_hvm_callback_vector 8 ++ ++#define XEN_FEATURES_PV_MASK (1U << XENFEAT_mmu_pt_update_preserve_ad) ++#define XEN_FEATURES_PVH_MASK ( (1U << XENFEAT_supervisor_mode_kernel) \ ++ | (1U << XENFEAT_hvm_callback_vector) ) + + /* virtualization types */ + enum { + VIRT_NONE = 0, + VIRT_PARA, +- VIRT_FULL ++ VIRT_FULL, ++ VIRT_CONT + }; +-const char *virt_types[] = { ++static const char *virt_types[] = { + [VIRT_NONE] = N_("none"), + [VIRT_PARA] = N_("para"), +- [VIRT_FULL] = N_("full") ++ [VIRT_FULL] = N_("full"), ++ [VIRT_CONT] = N_("container"), + }; + +-/* hypervisor vendors */ +-enum { +- HYPER_NONE = 0, +- HYPER_XEN, +- HYPER_KVM, +- HYPER_MSHV, +- HYPER_VMWARE, +- HYPER_IBM +-}; +-const char *hv_vendors[] = { ++static const char *hv_vendors[] = { + [HYPER_NONE] = NULL, + [HYPER_XEN] = "Xen", + [HYPER_KVM] = "KVM", + [HYPER_MSHV] = "Microsoft", + [HYPER_VMWARE] = "VMware", +- [HYPER_IBM] = "IBM" ++ [HYPER_IBM] = "IBM", ++ [HYPER_VSERVER] = "Linux-VServer", ++ [HYPER_UML] = "User-mode Linux", ++ [HYPER_INNOTEK] = "Innotek GmbH", ++ [HYPER_HITACHI] = "Hitachi", ++ [HYPER_PARALLELS] = "Parallels", ++ [HYPER_VBOX] = "Oracle", ++ [HYPER_OS400] = "OS/400", ++ [HYPER_PHYP] = "pHyp", ++ [HYPER_SPAR] = "Unisys s-Par", ++ [HYPER_WSL] = "Windows Subsystem for Linux" ++}; ++ ++static const int hv_vendor_pci[] = { ++ [HYPER_NONE] = 0x0000, ++ [HYPER_XEN] = 0x5853, ++ [HYPER_KVM] = 0x0000, ++ [HYPER_MSHV] = 0x1414, ++ [HYPER_VMWARE] = 0x15ad, ++ [HYPER_VBOX] = 0x80ee, ++}; ++ ++static const int hv_graphics_pci[] = { ++ [HYPER_NONE] = 0x0000, ++ [HYPER_XEN] = 0x0001, ++ [HYPER_KVM] = 0x0000, ++ [HYPER_MSHV] = 0x5353, ++ [HYPER_VMWARE] = 0x0710, ++ [HYPER_VBOX] = 0xbeef, + }; + + /* CPU modes */ +@@ -107,7 +169,7 @@ enum { + DISP_VERTICAL = 1 + }; + +-const char *disp_modes[] = { ++static const char *disp_modes[] = { + [DISP_HORIZONTAL] = N_("horizontal"), + [DISP_VERTICAL] = N_("vertical") + }; +@@ -126,7 +188,7 @@ struct polarization_modes { + char *readable; + }; + +-struct polarization_modes polar_modes[] = { ++static struct polarization_modes polar_modes[] = { + [POLAR_UNKNOWN] = {"U", "-"}, + [POLAR_VLOW] = {"VL", "vert-low"}, + [POLAR_VMEDIUM] = {"VM", "vert-medium"}, +@@ -138,6 +200,7 @@ struct polarization_modes polar_modes[] = { + struct lscpu_desc { + char *arch; + char *vendor; ++ char *machinetype; /* s390 */ + char *family; + char *model; + char *modelname; +@@ -148,9 +211,14 @@ struct lscpu_desc { + int hyper; /* hypervisor vendor ID */ + int virtype; /* VIRT_PARA|FULL|NONE ? */ + char *mhz; ++ char *dynamic_mhz; /* dynamic mega hertz (s390) */ ++ char *static_mhz; /* static mega hertz (s390) */ ++ char **maxmhz; /* maximum mega hertz */ ++ char **minmhz; /* minimum mega hertz */ + char *stepping; + char *bogomips; + char *flags; ++ char *mtid; /* maximum thread id (s390) */ + int dispatching; /* none, horizontal or vertical */ + int mode; /* rm, lm or/and tm */ + +@@ -159,33 +227,58 @@ struct lscpu_desc { + cpu_set_t *present; /* mask with present CPUs */ + cpu_set_t *online; /* mask with online CPUs */ + ++ int nthreads; /* number of online threads */ ++ ++ int ncaches; ++ struct cpu_cache *caches; ++ ++ int necaches; /* extra caches (s390) */ ++ struct cpu_cache *ecaches; ++ ++ /* ++ * All maps are sequentially indexed (0..ncpuspos), the array index ++ * does not have match with cpuX number as presented by kernel. You ++ * have to use real_cpu_num() to get the real cpuX number. ++ * ++ * For example, the possible system CPUs are: 1,3,5, it means that ++ * ncpuspos=3, so all arrays are in range 0..3. ++ */ ++ int *idx2cpunum; /* mapping index to CPU num */ ++ + int nnodes; /* number of NUMA modes */ + int *idx2nodenum; /* Support for discontinuous nodes */ + cpu_set_t **nodemaps; /* array with NUMA nodes */ + ++ /* drawers -- based on drawer_siblings (internal kernel map of cpuX's ++ * hardware threads within the same drawer */ ++ int ndrawers; /* number of all online drawers */ ++ cpu_set_t **drawermaps; /* unique drawer_siblings */ ++ int *drawerids; /* physical drawer ids */ ++ + /* books -- based on book_siblings (internal kernel map of cpuX's + * hardware threads within the same book */ + int nbooks; /* number of all online books */ + cpu_set_t **bookmaps; /* unique book_siblings */ ++ int *bookids; /* physical book ids */ + + /* sockets -- based on core_siblings (internal kernel map of cpuX's + * hardware threads within the same physical_package_id (socket)) */ + int nsockets; /* number of all online sockets */ + cpu_set_t **socketmaps; /* unique core_siblings */ ++ int *socketids; /* physical socket ids */ + +- /* cores -- based on thread_siblings (internel kernel map of cpuX's ++ /* cores -- based on thread_siblings (internal kernel map of cpuX's + * hardware threads within the same core as cpuX) */ + int ncores; /* number of all online cores */ + cpu_set_t **coremaps; /* unique thread_siblings */ +- +- int nthreads; /* number of online threads */ +- +- int ncaches; +- struct cpu_cache *caches; ++ int *coreids; /* physical core ids */ + + int *polarization; /* cpu polarization */ + int *addresses; /* physical cpu addresses */ + int *configured; /* cpu configured */ ++ int physsockets; /* Physical sockets (modules) */ ++ int physchips; /* Physical chips */ ++ int physcoresperchip; /* Physical cores per chip */ + }; + + enum { +@@ -205,7 +298,8 @@ struct lscpu_modifier { + unsigned int hex:1, /* print CPU masks rather than CPU lists */ + compat:1, /* use backwardly compatible format */ + online:1, /* print online CPUs */ +- offline:1; /* print offline CPUs */ ++ offline:1, /* print offline CPUs */ ++ physical:1; /* use physical numbers */ + }; + + static int maxcpus; /* size in bits of kernel cpu mask */ +@@ -217,6 +311,8 @@ static int maxcpus; /* size in bits of kernel cpu mask */ + ((_d) && (_d)->present ? \ + CPU_ISSET_S((_cpu), CPU_ALLOC_SIZE(maxcpus), (_d)->present) : 0) + ++#define real_cpu_num(_d, _i) ((_d)->idx2cpunum[(_i)]) ++ + /* + * IDs + */ +@@ -226,11 +322,14 @@ enum { + COL_SOCKET, + COL_NODE, + COL_BOOK, ++ COL_DRAWER, + COL_CACHE, + COL_POLARIZATION, + COL_ADDRESS, + COL_CONFIGURED, + COL_ONLINE, ++ COL_MAXMHZ, ++ COL_MINMHZ, + }; + + /* column description +@@ -249,11 +348,14 @@ static struct lscpu_coldesc coldescs[] = + [COL_SOCKET] = { "SOCKET", N_("logical socket number") }, + [COL_NODE] = { "NODE", N_("logical NUMA node number") }, + [COL_BOOK] = { "BOOK", N_("logical book number") }, ++ [COL_DRAWER] = { "DRAWER", N_("logical drawer number") }, + [COL_CACHE] = { "CACHE", N_("shows how caches are shared between CPUs") }, + [COL_POLARIZATION] = { "POLARIZATION", N_("CPU dispatching mode on virtual hardware") }, + [COL_ADDRESS] = { "ADDRESS", N_("physical address of a CPU") }, + [COL_CONFIGURED] = { "CONFIGURED", N_("shows if the hypervisor has allocated the CPU") }, +- [COL_ONLINE] = { "ONLINE", N_("shows if Linux currently makes use of the CPU") } ++ [COL_ONLINE] = { "ONLINE", N_("shows if Linux currently makes use of the CPU") }, ++ [COL_MAXMHZ] = { "MAXMHZ", N_("shows the maximum MHz of the CPU") }, ++ [COL_MINMHZ] = { "MINMHZ", N_("shows the minimum MHz of the CPU") } + }; + + static int +@@ -282,7 +384,8 @@ lookup(char *line, char *pattern, char **value) + char *p, *v; + int len = strlen(pattern); + +- if (!*line) ++ /* don't re-fill already found tags, first one wins */ ++ if (!*line || *value) + return 0; + + /* pattern */ +@@ -313,6 +416,63 @@ lookup(char *line, char *pattern, char **value) + return 1; + } + ++/* Parse extra cache lines contained within /proc/cpuinfo but which are not ++ * part of the cache topology information within the sysfs filesystem. ++ * This is true for all shared caches on e.g. s390. When there are layers of ++ * hypervisors in between it is not knows which CPUs share which caches. ++ * Therefore information about shared caches is only available in ++ * /proc/cpuinfo. ++ * Format is: ++ * "cache<nr> : level=<lvl> type=<type> scope=<scope> size=<size> line_size=<lsz> associativity=<as>" ++ */ ++static int ++lookup_cache(char *line, struct lscpu_desc *desc) ++{ ++ struct cpu_cache *cache; ++ long long size; ++ char *p, type; ++ int level; ++ ++ /* Make sure line starts with "cache<nr> :" */ ++ if (strncmp(line, "cache", 5)) ++ return 0; ++ for (p = line + 5; isdigit(*p); p++); ++ for (; isspace(*p); p++); ++ if (*p != ':') ++ return 0; ++ ++ p = strstr(line, "scope=") + 6; ++ /* Skip private caches, also present in sysfs */ ++ if (!p || strncmp(p, "Private", 7) == 0) ++ return 0; ++ p = strstr(line, "level="); ++ if (!p || sscanf(p, "level=%d", &level) != 1) ++ return 0; ++ p = strstr(line, "type=") + 5; ++ if (!p || !*p) ++ return 0; ++ type = 0; ++ if (strncmp(p, "Data", 4) == 0) ++ type = 'd'; ++ if (strncmp(p, "Instruction", 11) == 0) ++ type = 'i'; ++ p = strstr(line, "size="); ++ if (!p || sscanf(p, "size=%lld", &size) != 1) ++ return 0; ++ ++ desc->necaches++; ++ desc->ecaches = xrealloc(desc->ecaches, ++ desc->necaches * sizeof(struct cpu_cache)); ++ cache = &desc->ecaches[desc->necaches - 1]; ++ memset(cache, 0 , sizeof(*cache)); ++ if (type) ++ xasprintf(&cache->name, "L%d%c", level, type); ++ else ++ xasprintf(&cache->name, "L%d", level); ++ xasprintf(&cache->size, "%lldK", size); ++ return 1; ++} ++ + /* Don't init the mode for platforms where we are not able to + * detect that CPU supports 64-bit mode. + */ +@@ -338,6 +498,45 @@ init_mode(struct lscpu_modifier *mod) + return m; + } + ++#if defined(HAVE_LIBRTAS) ++#define PROCESSOR_MODULE_INFO 43 ++static int strbe16toh(const char *buf, int offset) ++{ ++ return (buf[offset] << 8) + buf[offset+1]; ++} ++ ++static void read_physical_info_powerpc(struct lscpu_desc *desc) ++{ ++ char buf[BUFSIZ]; ++ int rc, len, ntypes; ++ ++ desc->physsockets = desc->physchips = desc->physcoresperchip = 0; ++ ++ rc = rtas_get_sysparm(PROCESSOR_MODULE_INFO, sizeof(buf), buf); ++ if (rc < 0) ++ return; ++ ++ len = strbe16toh(buf, 0); ++ if (len < 8) ++ return; ++ ++ ntypes = strbe16toh(buf, 2); ++ ++ assert(ntypes <= 1); ++ if (!ntypes) ++ return; ++ ++ desc->physsockets = strbe16toh(buf, 4); ++ desc->physchips = strbe16toh(buf, 6); ++ desc->physcoresperchip = strbe16toh(buf, 8); ++} ++#else ++static void read_physical_info_powerpc( ++ struct lscpu_desc *desc __attribute__((__unused__))) ++{ ++} ++#endif ++ + static void + read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod) + { +@@ -361,13 +560,20 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod) + else if (lookup(buf, "model name", &desc->modelname)) ; + else if (lookup(buf, "stepping", &desc->stepping)) ; + else if (lookup(buf, "cpu MHz", &desc->mhz)) ; ++ else if (lookup(buf, "cpu MHz dynamic", &desc->dynamic_mhz)) ; /* s390 */ ++ else if (lookup(buf, "cpu MHz static", &desc->static_mhz)) ; /* s390 */ + else if (lookup(buf, "flags", &desc->flags)) ; /* x86 */ + else if (lookup(buf, "features", &desc->flags)) ; /* s390 */ ++ else if (lookup(buf, "Features", &desc->flags)) ; /* aarch64 */ + else if (lookup(buf, "type", &desc->flags)) ; /* sparc64 */ + else if (lookup(buf, "bogomips", &desc->bogomips)) ; ++ else if (lookup(buf, "BogoMIPS", &desc->bogomips)) ; /* aarch64 */ + else if (lookup(buf, "bogomips per cpu", &desc->bogomips)) ; /* s390 */ + else if (lookup(buf, "cpu", &desc->cpu)) ; + else if (lookup(buf, "revision", &desc->revision)) ; ++ else if (lookup(buf, "CPU revision", &desc->revision)) ; /* aarch64 */ ++ else if (lookup(buf, "max thread id", &desc->mtid)) ; /* s390 */ ++ else if (lookup_cache(buf, desc)) ; + else + continue; + } +@@ -397,9 +603,9 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod) + + fclose(fp); + +- if (path_exist(_PATH_SYS_SYSTEM "/cpu/kernel_max")) ++ if (path_exist(_PATH_SYS_CPU "/kernel_max")) + /* note that kernel_max is maximum index [NR_CPUS-1] */ +- maxcpus = path_read_s32(_PATH_SYS_SYSTEM "/cpu/kernel_max") + 1; ++ maxcpus = path_read_s32(_PATH_SYS_CPU "/kernel_max") + 1; + + else if (mod->system == SYSTEM_LIVE) + /* the root is '/' so we are working with data from the current kernel */ +@@ -412,32 +618,49 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod) + + setsize = CPU_ALLOC_SIZE(maxcpus); + +- if (path_exist(_PATH_SYS_SYSTEM "/cpu/possible")) { +- cpu_set_t *tmp = path_read_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/possible"); ++ if (path_exist(_PATH_SYS_CPU "/possible")) { ++ cpu_set_t *tmp = path_read_cpulist(maxcpus, _PATH_SYS_CPU "/possible"); ++ int num, idx; ++ + desc->ncpuspos = CPU_COUNT_S(setsize, tmp); ++ desc->idx2cpunum = xcalloc(desc->ncpuspos, sizeof(int)); ++ ++ for (num = 0, idx = 0; num < maxcpus; num++) { ++ if (CPU_ISSET(num, tmp)) ++ desc->idx2cpunum[idx++] = num; ++ } + cpuset_free(tmp); + } else + err(EXIT_FAILURE, _("failed to determine number of CPUs: %s"), +- _PATH_SYS_SYSTEM "/cpu/possible"); ++ _PATH_SYS_CPU "/possible"); + + + /* get mask for present CPUs */ +- if (path_exist(_PATH_SYS_SYSTEM "/cpu/present")) { +- desc->present = path_read_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/present"); ++ if (path_exist(_PATH_SYS_CPU "/present")) { ++ desc->present = path_read_cpulist(maxcpus, _PATH_SYS_CPU "/present"); + desc->ncpus = CPU_COUNT_S(setsize, desc->present); + } + + /* get mask for online CPUs */ +- if (path_exist(_PATH_SYS_SYSTEM "/cpu/online")) { +- desc->online = path_read_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/online"); ++ if (path_exist(_PATH_SYS_CPU "/online")) { ++ desc->online = path_read_cpulist(maxcpus, _PATH_SYS_CPU "/online"); + desc->nthreads = CPU_COUNT_S(setsize, desc->online); + } + + /* get dispatching mode */ +- if (path_exist(_PATH_SYS_SYSTEM "/cpu/dispatching")) +- desc->dispatching = path_read_s32(_PATH_SYS_SYSTEM "/cpu/dispatching"); ++ if (path_exist(_PATH_SYS_CPU "/dispatching")) ++ desc->dispatching = path_read_s32(_PATH_SYS_CPU "/dispatching"); + else + desc->dispatching = -1; ++ ++ if (mod->system == SYSTEM_LIVE) ++ read_physical_info_powerpc(desc); ++ ++ if ((fp = path_fopen("r", 0, _PATH_PROC_SYSINFO))) { ++ while (fgets(buf, sizeof(buf), fp) != NULL && !desc->machinetype) ++ lookup(buf, "Type", &desc->machinetype); ++ fclose(fp); ++ } + } + + static int +@@ -483,10 +706,9 @@ cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx, + __asm__( + #if defined(__PIC__) && defined(__i386__) + /* x86 PIC cannot clobber ebx -- gcc bitches */ +- "pushl %%ebx;" ++ "xchg %%ebx, %%esi;" + "cpuid;" +- "movl %%ebx, %%esi;" +- "popl %%ebx;" ++ "xchg %%esi, %%ebx;" + : "=S" (*ebx), + #else + "cpuid;" +@@ -523,34 +745,230 @@ read_hypervisor_cpuid(struct lscpu_desc *desc) + desc->hyper = HYPER_MSHV; + else if (!strncmp("VMwareVMware", hyper_vendor_id, 12)) + desc->hyper = HYPER_VMWARE; ++ else if (!strncmp("UnisysSpar64", hyper_vendor_id, 12)) ++ desc->hyper = HYPER_SPAR; + } + +-#else /* ! __x86_64__ */ ++#else /* ! (__x86_64__ || __i386__) */ + static void + read_hypervisor_cpuid(struct lscpu_desc *desc __attribute__((__unused__))) + { + } + #endif + ++static int is_compatible(const char *path, const char *str) ++{ ++ FILE *fd = path_fopen("r", 0, "%s", path); ++ ++ if (fd) { ++ char buf[256]; ++ size_t i, len; ++ ++ memset(buf, 0, sizeof(buf)); ++ len = fread(buf, 1, sizeof(buf) - 1, fd); ++ fclose(fd); ++ ++ for (i = 0; i < len;) { ++ if (!strcmp(&buf[i], str)) ++ return 1; ++ i += strlen(&buf[i]); ++ i++; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++read_hypervisor_powerpc(struct lscpu_desc *desc) ++{ ++ assert(!desc->hyper); ++ ++ /* IBM iSeries: legacy, para-virtualized on top of OS/400 */ ++ if (path_exist("/proc/iSeries")) { ++ desc->hyper = HYPER_OS400; ++ desc->virtype = VIRT_PARA; ++ ++ /* PowerNV (POWER Non-Virtualized, bare-metal) */ ++ } else if (is_compatible(_PATH_PROC_DEVICETREE "/compatible", "ibm,powernv")) { ++ desc->hyper = HYPER_NONE; ++ desc->virtype = VIRT_NONE; ++ ++ /* PowerVM (IBM's proprietary hypervisor, aka pHyp) */ ++ } else if (path_exist(_PATH_PROC_DEVICETREE "/ibm,partition-name") ++ && path_exist(_PATH_PROC_DEVICETREE "/hmc-managed?") ++ && !path_exist(_PATH_PROC_DEVICETREE "/chosen/qemu,graphic-width")) { ++ FILE *fd; ++ desc->hyper = HYPER_PHYP; ++ desc->virtype = VIRT_PARA; ++ fd = path_fopen("r", 0, _PATH_PROC_DEVICETREE "/ibm,partition-name"); ++ if (fd) { ++ char buf[256]; ++ if (fscanf(fd, "%255s", buf) == 1 && !strcmp(buf, "full")) ++ desc->virtype = VIRT_NONE; ++ fclose(fd); ++ } ++ ++ /* Qemu */ ++ } else if (is_compatible(_PATH_PROC_DEVICETREE "/compatible", "qemu,pseries")) { ++ desc->hyper = HYPER_KVM; ++ desc->virtype = VIRT_PARA; ++ } ++ return desc->hyper; ++} ++ ++#ifdef INCLUDE_VMWARE_BDOOR ++ ++#define VMWARE_BDOOR_MAGIC 0x564D5868 ++#define VMWARE_BDOOR_PORT 0x5658 ++#define VMWARE_BDOOR_CMD_GETVERSION 10 ++ ++static UL_ASAN_BLACKLIST ++void vmware_bdoor(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) ++{ ++ __asm__( ++#if defined(__PIC__) && defined(__i386__) ++ /* x86 PIC cannot clobber ebx -- gcc bitches */ ++ "xchg %%ebx, %%esi;" ++ "inl (%%dx), %%eax;" ++ "xchg %%esi, %%ebx;" ++ : "=S" (*ebx), ++#else ++ "inl (%%dx), %%eax;" ++ : "=b" (*ebx), ++#endif ++ "=a" (*eax), ++ "=c" (*ecx), ++ "=d" (*edx) ++ : "0" (VMWARE_BDOOR_MAGIC), ++ "1" (VMWARE_BDOOR_CMD_GETVERSION), ++ "2" (VMWARE_BDOOR_PORT), ++ "3" (0) ++ : "memory"); ++} ++ ++static jmp_buf segv_handler_env; ++ ++static void ++segv_handler(__attribute__((__unused__)) int sig, ++ __attribute__((__unused__)) siginfo_t *info, ++ __attribute__((__unused__)) void *ignored) ++{ ++ siglongjmp(segv_handler_env, 1); ++} ++ ++static int ++is_vmware_platform(void) ++{ ++ uint32_t eax, ebx, ecx, edx; ++ struct sigaction act, oact; ++ ++ /* ++ * FIXME: Not reliable for non-root users. Note it works as expected if ++ * vmware_bdoor() is not optimized for PIE, but then it fails to build ++ * on 32bit x86 systems. See lscpu git log for more details (commit ++ * 7845b91dbc7690064a2be6df690e4aaba728fb04). kzak [3-Nov-2016] ++ */ ++ if (getuid() != 0) ++ return 0; ++ ++ /* ++ * The assembly routine for vmware detection works ++ * fine under vmware, even if ran as regular user. But ++ * on real HW or under other hypervisors, it segfaults (which is ++ * expected). So we temporarily install SIGSEGV handler to catch ++ * the signal. All this magic is needed because lscpu ++ * isn't supposed to require root privileges. ++ */ ++ if (sigsetjmp(segv_handler_env, 1)) ++ return 0; ++ ++ memset(&act, 0, sizeof(act)); ++ act.sa_sigaction = segv_handler; ++ act.sa_flags = SA_SIGINFO; ++ ++ if (sigaction(SIGSEGV, &act, &oact)) ++ err(EXIT_FAILURE, _("cannot set signal handler")); ++ ++ vmware_bdoor(&eax, &ebx, &ecx, &edx); ++ ++ if (sigaction(SIGSEGV, &oact, NULL)) ++ err(EXIT_FAILURE, _("cannot restore signal handler")); ++ ++ return eax != (uint32_t)-1 && ebx == VMWARE_BDOOR_MAGIC; ++} ++ ++#else /* ! INCLUDE_VMWARE_BDOOR */ ++ ++static int ++is_vmware_platform(void) ++{ ++ return 0; ++} ++ ++#endif /* INCLUDE_VMWARE_BDOOR */ ++ + static void + read_hypervisor(struct lscpu_desc *desc, struct lscpu_modifier *mod) + { +- if (mod->system != SYSTEM_SNAPSHOT) ++ FILE *fd; ++ ++ /* We have to detect WSL first. is_vmware_platform() crashes on Windows 10. */ ++ ++ if ((fd = path_fopen("r", 0, _PATH_PROC_OSRELEASE))) { ++ char buf[256]; ++ ++ if (fgets(buf, sizeof(buf), fd) != NULL) { ++ if (strstr(buf, "Microsoft")) { ++ desc->hyper = HYPER_WSL; ++ desc->virtype = VIRT_CONT; ++ } ++ } ++ fclose(fd); ++ if (desc->virtype) ++ return; ++ } ++ ++ if (mod->system != SYSTEM_SNAPSHOT) { + read_hypervisor_cpuid(desc); ++ if (!desc->hyper) ++ desc->hyper = read_hypervisor_dmi(); ++ if (!desc->hyper && is_vmware_platform()) ++ desc->hyper = HYPER_VMWARE; ++ } + +- if (desc->hyper) +- /* hvm */ ++ if (desc->hyper) { + desc->virtype = VIRT_FULL; + ++ if (desc->hyper == HYPER_XEN) { ++ uint32_t features; ++ ++ fd = path_fopen("r", 0, _PATH_SYS_HYP_FEATURES); ++ if (fd && fscanf(fd, "%x", &features) == 1) { ++ /* Xen PV domain */ ++ if (features & XEN_FEATURES_PV_MASK) ++ desc->virtype = VIRT_PARA; ++ /* Xen PVH domain */ ++ else if ((features & XEN_FEATURES_PVH_MASK) ++ == XEN_FEATURES_PVH_MASK) ++ desc->virtype = VIRT_PARA; ++ fclose(fd); ++ } else { ++ err(EXIT_FAILURE, _("failed to read from: %s"), ++ _PATH_SYS_HYP_FEATURES); ++ } ++ } ++ } else if (read_hypervisor_powerpc(desc) > 0) {} ++ ++ /* Xen para-virt or dom0 */ + else if (path_exist(_PATH_PROC_XEN)) { +- /* Xen para-virt or dom0 */ +- FILE *fd = path_fopen("r", 0, _PATH_PROC_XENCAP); + int dom0 = 0; ++ fd = path_fopen("r", 0, _PATH_PROC_XENCAP); + + if (fd) { + char buf[256]; + +- if (fscanf(fd, "%s", buf) == 1 && ++ if (fscanf(fd, "%255s", buf) == 1 && + !strcmp(buf, "control_d")) + dom0 = 1; + fclose(fd); +@@ -558,16 +976,21 @@ read_hypervisor(struct lscpu_desc *desc, struct lscpu_modifier *mod) + desc->virtype = dom0 ? VIRT_NONE : VIRT_PARA; + desc->hyper = HYPER_XEN; + +- } else if (has_pci_device(0x5853, 0x0001)) { +- /* Xen full-virt on non-x86_64 */ ++ /* Xen full-virt on non-x86_64 */ ++ } else if (has_pci_device( hv_vendor_pci[HYPER_XEN], hv_graphics_pci[HYPER_XEN])) { + desc->hyper = HYPER_XEN; + desc->virtype = VIRT_FULL; +- } else if (path_exist(_PATH_PROC_SYSINFO)) { +- FILE *fd = path_fopen("r", 0, _PATH_PROC_SYSINFO); ++ } else if (has_pci_device( hv_vendor_pci[HYPER_VMWARE], hv_graphics_pci[HYPER_VMWARE])) { ++ desc->hyper = HYPER_VMWARE; ++ desc->virtype = VIRT_FULL; ++ } else if (has_pci_device( hv_vendor_pci[HYPER_VBOX], hv_graphics_pci[HYPER_VBOX])) { ++ desc->hyper = HYPER_VBOX; ++ desc->virtype = VIRT_FULL; ++ ++ /* IBM PR/SM */ ++ } else if ((fd = path_fopen("r", 0, _PATH_PROC_SYSINFO))) { + char buf[BUFSIZ]; + +- if (!fd) +- return; + desc->hyper = HYPER_IBM; + desc->hypervisor = "PR/SM"; + desc->virtype = VIRT_FULL; +@@ -597,6 +1020,45 @@ read_hypervisor(struct lscpu_desc *desc, struct lscpu_modifier *mod) + } + fclose(fd); + } ++ ++ /* OpenVZ/Virtuozzo - /proc/vz dir should exist ++ * /proc/bc should not */ ++ else if (path_exist(_PATH_PROC_VZ) && !path_exist(_PATH_PROC_BC)) { ++ desc->hyper = HYPER_PARALLELS; ++ desc->virtype = VIRT_CONT; ++ ++ /* IBM */ ++ } else if (desc->vendor && ++ (strcmp(desc->vendor, "PowerVM Lx86") == 0 || ++ strcmp(desc->vendor, "IBM/S390") == 0)) { ++ desc->hyper = HYPER_IBM; ++ desc->virtype = VIRT_FULL; ++ ++ /* User-mode-linux */ ++ } else if (desc->modelname && strstr(desc->modelname, "UML")) { ++ desc->hyper = HYPER_UML; ++ desc->virtype = VIRT_PARA; ++ ++ /* Linux-VServer */ ++ } else if ((fd = path_fopen("r", 0, _PATH_PROC_STATUS))) { ++ char buf[BUFSIZ]; ++ char *val = NULL; ++ ++ while (fgets(buf, sizeof(buf), fd) != NULL) { ++ if (lookup(buf, "VxID", &val)) ++ break; ++ } ++ fclose(fd); ++ ++ if (val) { ++ while (isdigit(*val)) ++ ++val; ++ if (!*val) { ++ desc->hyper = HYPER_VSERVER; ++ desc->virtype = VIRT_CONT; ++ } ++ } ++ } + } + + /* add @set to the @ary, unnecessary set is deallocated. */ +@@ -622,9 +1084,12 @@ static int add_cpuset_to_array(cpu_set_t **ary, int *items, cpu_set_t *set) + } + + static void +-read_topology(struct lscpu_desc *desc, int num) ++read_topology(struct lscpu_desc *desc, int idx) + { +- cpu_set_t *thread_siblings, *core_siblings, *book_siblings; ++ cpu_set_t *thread_siblings, *core_siblings; ++ cpu_set_t *book_siblings, *drawer_siblings; ++ int coreid, socketid, bookid, drawerid; ++ int i, num = real_cpu_num(desc, idx); + + if (!path_exist(_PATH_SYS_CPU "/cpu%d/topology/thread_siblings", num)) + return; +@@ -634,13 +1099,32 @@ read_topology(struct lscpu_desc *desc, int num) + core_siblings = path_read_cpuset(maxcpus, _PATH_SYS_CPU + "/cpu%d/topology/core_siblings", num); + book_siblings = NULL; +- if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/book_siblings", num)) { ++ if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/book_siblings", num)) + book_siblings = path_read_cpuset(maxcpus, _PATH_SYS_CPU + "/cpu%d/topology/book_siblings", num); +- } ++ drawer_siblings = NULL; ++ if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/drawer_siblings", num)) ++ drawer_siblings = path_read_cpuset(maxcpus, _PATH_SYS_CPU ++ "/cpu%d/topology/drawer_siblings", num); ++ coreid = -1; ++ if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/core_id", num)) ++ coreid = path_read_s32(_PATH_SYS_CPU ++ "/cpu%d/topology/core_id", num); ++ socketid = -1; ++ if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/physical_package_id", num)) ++ socketid = path_read_s32(_PATH_SYS_CPU ++ "/cpu%d/topology/physical_package_id", num); ++ bookid = -1; ++ if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/book_id", num)) ++ bookid = path_read_s32(_PATH_SYS_CPU ++ "/cpu%d/topology/book_id", num); ++ drawerid = -1; ++ if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/drawer_id", num)) ++ drawerid = path_read_s32(_PATH_SYS_CPU ++ "/cpu%d/topology/drawer_id", num); + + if (!desc->coremaps) { +- int nbooks, nsockets, ncores, nthreads; ++ int ndrawers, nbooks, nsockets, ncores, nthreads; + size_t setsize = CPU_ALLOC_SIZE(maxcpus); + + /* threads within one core */ +@@ -666,12 +1150,17 @@ read_topology(struct lscpu_desc *desc, int num) + if (!nbooks) + nbooks = 1; + ++ /* number of drawers */ ++ ndrawers = desc->ncpus / nbooks / nthreads / ncores / nsockets; ++ if (!ndrawers) ++ ndrawers = 1; ++ + /* all threads, see also read_basicinfo() + * -- fallback for kernels without + * /sys/devices/system/cpu/online. + */ + if (!desc->nthreads) +- desc->nthreads = nbooks * nsockets * ncores * nthreads; ++ desc->nthreads = ndrawers * nbooks * nsockets * ncores * nthreads; + + /* For each map we make sure that it can have up to ncpuspos + * entries. This is because we cannot reliably calculate the +@@ -681,19 +1170,43 @@ read_topology(struct lscpu_desc *desc, int num) + */ + desc->coremaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *)); + desc->socketmaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *)); +- if (book_siblings) ++ desc->coreids = xcalloc(desc->ncpuspos, sizeof(*desc->drawerids)); ++ desc->socketids = xcalloc(desc->ncpuspos, sizeof(*desc->drawerids)); ++ for (i = 0; i < desc->ncpuspos; i++) ++ desc->coreids[i] = desc->socketids[i] = -1; ++ if (book_siblings) { + desc->bookmaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *)); ++ desc->bookids = xcalloc(desc->ncpuspos, sizeof(*desc->drawerids)); ++ for (i = 0; i < desc->ncpuspos; i++) ++ desc->bookids[i] = -1; ++ } ++ if (drawer_siblings) { ++ desc->drawermaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *)); ++ desc->drawerids = xcalloc(desc->ncpuspos, sizeof(*desc->drawerids)); ++ for (i = 0; i < desc->ncpuspos; i++) ++ desc->drawerids[i] = -1; ++ } + } + + add_cpuset_to_array(desc->socketmaps, &desc->nsockets, core_siblings); ++ desc->coreids[idx] = coreid; + add_cpuset_to_array(desc->coremaps, &desc->ncores, thread_siblings); +- if (book_siblings) ++ desc->socketids[idx] = socketid; ++ if (book_siblings) { + add_cpuset_to_array(desc->bookmaps, &desc->nbooks, book_siblings); ++ desc->bookids[idx] = bookid; ++ } ++ if (drawer_siblings) { ++ add_cpuset_to_array(desc->drawermaps, &desc->ndrawers, drawer_siblings); ++ desc->drawerids[idx] = drawerid; ++ } + } ++ + static void +-read_polarization(struct lscpu_desc *desc, int num) ++read_polarization(struct lscpu_desc *desc, int idx) + { + char mode[64]; ++ int num = real_cpu_num(desc, idx); + + if (desc->dispatching < 0) + return; +@@ -703,35 +1216,67 @@ read_polarization(struct lscpu_desc *desc, int num) + desc->polarization = xcalloc(desc->ncpuspos, sizeof(int)); + path_read_str(mode, sizeof(mode), _PATH_SYS_CPU "/cpu%d/polarization", num); + if (strncmp(mode, "vertical:low", sizeof(mode)) == 0) +- desc->polarization[num] = POLAR_VLOW; ++ desc->polarization[idx] = POLAR_VLOW; + else if (strncmp(mode, "vertical:medium", sizeof(mode)) == 0) +- desc->polarization[num] = POLAR_VMEDIUM; ++ desc->polarization[idx] = POLAR_VMEDIUM; + else if (strncmp(mode, "vertical:high", sizeof(mode)) == 0) +- desc->polarization[num] = POLAR_VHIGH; ++ desc->polarization[idx] = POLAR_VHIGH; + else if (strncmp(mode, "horizontal", sizeof(mode)) == 0) +- desc->polarization[num] = POLAR_HORIZONTAL; ++ desc->polarization[idx] = POLAR_HORIZONTAL; + else +- desc->polarization[num] = POLAR_UNKNOWN; ++ desc->polarization[idx] = POLAR_UNKNOWN; + } + + static void +-read_address(struct lscpu_desc *desc, int num) ++read_address(struct lscpu_desc *desc, int idx) + { ++ int num = real_cpu_num(desc, idx); ++ + if (!path_exist(_PATH_SYS_CPU "/cpu%d/address", num)) + return; + if (!desc->addresses) + desc->addresses = xcalloc(desc->ncpuspos, sizeof(int)); +- desc->addresses[num] = path_read_s32(_PATH_SYS_CPU "/cpu%d/address", num); ++ desc->addresses[idx] = path_read_s32(_PATH_SYS_CPU "/cpu%d/address", num); + } + + static void +-read_configured(struct lscpu_desc *desc, int num) ++read_configured(struct lscpu_desc *desc, int idx) + { ++ int num = real_cpu_num(desc, idx); ++ + if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", num)) + return; + if (!desc->configured) + desc->configured = xcalloc(desc->ncpuspos, sizeof(int)); +- desc->configured[num] = path_read_s32(_PATH_SYS_CPU "/cpu%d/configure", num); ++ desc->configured[idx] = path_read_s32(_PATH_SYS_CPU "/cpu%d/configure", num); ++} ++ ++static void ++read_max_mhz(struct lscpu_desc *desc, int idx) ++{ ++ int num = real_cpu_num(desc, idx); ++ ++ if (!path_exist(_PATH_SYS_CPU "/cpu%d/cpufreq/cpuinfo_max_freq", num)) ++ return; ++ if (!desc->maxmhz) ++ desc->maxmhz = xcalloc(desc->ncpuspos, sizeof(char *)); ++ xasprintf(&(desc->maxmhz[idx]), "%.4f", ++ (float)path_read_s32(_PATH_SYS_CPU ++ "/cpu%d/cpufreq/cpuinfo_max_freq", num) / 1000); ++} ++ ++static void ++read_min_mhz(struct lscpu_desc *desc, int idx) ++{ ++ int num = real_cpu_num(desc, idx); ++ ++ if (!path_exist(_PATH_SYS_CPU "/cpu%d/cpufreq/cpuinfo_min_freq", num)) ++ return; ++ if (!desc->minmhz) ++ desc->minmhz = xcalloc(desc->ncpuspos, sizeof(char *)); ++ xasprintf(&(desc->minmhz[idx]), "%.4f", ++ (float)path_read_s32(_PATH_SYS_CPU ++ "/cpu%d/cpufreq/cpuinfo_min_freq", num) / 1000); + } + + static int +@@ -744,13 +1289,14 @@ cachecmp(const void *a, const void *b) + } + + static void +-read_cache(struct lscpu_desc *desc, int num) ++read_cache(struct lscpu_desc *desc, int idx) + { + char buf[256]; + int i; ++ int num = real_cpu_num(desc, idx); + + if (!desc->ncaches) { +- while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d/cache/index%d", ++ while(path_exist(_PATH_SYS_CPU "/cpu%d/cache/index%d", + num, desc->ncaches)) + desc->ncaches++; + +@@ -763,7 +1309,7 @@ read_cache(struct lscpu_desc *desc, int num) + struct cpu_cache *ca = &desc->caches[i]; + cpu_set_t *map; + +- if (!path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d/cache/index%d", ++ if (!path_exist(_PATH_SYS_CPU "/cpu%d/cache/index%d", + num, i)) + continue; + if (!ca->name) { +@@ -791,10 +1337,13 @@ read_cache(struct lscpu_desc *desc, int num) + ca->name = xstrdup(buf); + + /* cache size */ +- path_read_str(buf, sizeof(buf), +- _PATH_SYS_CPU "/cpu%d/cache/index%d/size", +- num, i); +- ca->size = xstrdup(buf); ++ if (path_exist(_PATH_SYS_CPU "/cpu%d/cache/index%d/size",num, i)) { ++ path_read_str(buf, sizeof(buf), ++ _PATH_SYS_CPU "/cpu%d/cache/index%d/size", num, i); ++ ca->size = xstrdup(buf); ++ } else { ++ ca->size = xstrdup("unknown size"); ++ } + } + + /* information about how CPUs share different caches */ +@@ -867,17 +1416,18 @@ read_nodes(struct lscpu_desc *desc) + /* information about how nodes share different CPUs */ + for (i = 0; i < desc->nnodes; i++) + desc->nodemaps[i] = path_read_cpuset(maxcpus, +- _PATH_SYS_SYSTEM "/node/node%d/cpumap", ++ _PATH_SYS_NODE "/node%d/cpumap", + desc->idx2nodenum[i]); + } + + static char * +-get_cell_data(struct lscpu_desc *desc, int cpu, int col, ++get_cell_data(struct lscpu_desc *desc, int idx, int col, + struct lscpu_modifier *mod, + char *buf, size_t bufsz) + { + size_t setsize = CPU_ALLOC_SIZE(maxcpus); +- size_t idx; ++ size_t i; ++ int cpu = real_cpu_num(desc, idx); + + *buf = '\0'; + +@@ -886,24 +1436,57 @@ get_cell_data(struct lscpu_desc *desc, int cpu, int col, + snprintf(buf, bufsz, "%d", cpu); + break; + case COL_CORE: +- if (cpuset_ary_isset(cpu, desc->coremaps, +- desc->ncores, setsize, &idx) == 0) +- snprintf(buf, bufsz, "%zd", idx); ++ if (mod->physical) { ++ if (desc->coreids[idx] == -1) ++ snprintf(buf, bufsz, "-"); ++ else ++ snprintf(buf, bufsz, "%d", desc->coreids[idx]); ++ } else { ++ if (cpuset_ary_isset(cpu, desc->coremaps, ++ desc->ncores, setsize, &i) == 0) ++ snprintf(buf, bufsz, "%zu", i); ++ } + break; + case COL_SOCKET: +- if (cpuset_ary_isset(cpu, desc->socketmaps, +- desc->nsockets, setsize, &idx) == 0) +- snprintf(buf, bufsz, "%zd", idx); ++ if (mod->physical) { ++ if (desc->socketids[idx] == -1) ++ snprintf(buf, bufsz, "-"); ++ else ++ snprintf(buf, bufsz, "%d", desc->socketids[idx]); ++ } else { ++ if (cpuset_ary_isset(cpu, desc->socketmaps, ++ desc->nsockets, setsize, &i) == 0) ++ snprintf(buf, bufsz, "%zu", i); ++ } + break; + case COL_NODE: + if (cpuset_ary_isset(cpu, desc->nodemaps, +- desc->nnodes, setsize, &idx) == 0) +- snprintf(buf, bufsz, "%d", desc->idx2nodenum[idx]); ++ desc->nnodes, setsize, &i) == 0) ++ snprintf(buf, bufsz, "%d", desc->idx2nodenum[i]); ++ break; ++ case COL_DRAWER: ++ if (mod->physical) { ++ if (desc->drawerids[idx] == -1) ++ snprintf(buf, bufsz, "-"); ++ else ++ snprintf(buf, bufsz, "%d", desc->drawerids[idx]); ++ } else { ++ if (cpuset_ary_isset(cpu, desc->drawermaps, ++ desc->ndrawers, setsize, &i) == 0) ++ snprintf(buf, bufsz, "%zu", i); ++ } + break; + case COL_BOOK: +- if (cpuset_ary_isset(cpu, desc->bookmaps, +- desc->nbooks, setsize, &idx) == 0) +- snprintf(buf, bufsz, "%zd", idx); ++ if (mod->physical) { ++ if (desc->bookids[idx] == -1) ++ snprintf(buf, bufsz, "-"); ++ else ++ snprintf(buf, bufsz, "%d", desc->bookids[idx]); ++ } else { ++ if (cpuset_ary_isset(cpu, desc->bookmaps, ++ desc->nbooks, setsize, &i) == 0) ++ snprintf(buf, bufsz, "%zu", i); ++ } + break; + case COL_CACHE: + { +@@ -915,24 +1498,26 @@ get_cell_data(struct lscpu_desc *desc, int cpu, int col, + struct cpu_cache *ca = &desc->caches[j]; + + if (cpuset_ary_isset(cpu, ca->sharedmaps, +- ca->nsharedmaps, setsize, &idx) == 0) { +- int x = snprintf(p, sz, "%zd", idx); +- if (x <= 0 || (size_t) x + 2 >= sz) ++ ca->nsharedmaps, setsize, &i) == 0) { ++ int x = snprintf(p, sz, "%zu", i); ++ if (x < 0 || (size_t) x >= sz) + return NULL; + p += x; + sz -= x; + } + if (j != 0) { ++ if (sz < 2) ++ return NULL; + *p++ = mod->compat ? ',' : ':'; + *p = '\0'; +- sz++; ++ sz--; + } + } + break; + } + case COL_POLARIZATION: + if (desc->polarization) { +- int x = desc->polarization[cpu]; ++ int x = desc->polarization[idx]; + + snprintf(buf, bufsz, "%s", + mod->mode == OUTPUT_PARSABLE ? +@@ -942,28 +1527,36 @@ get_cell_data(struct lscpu_desc *desc, int cpu, int col, + break; + case COL_ADDRESS: + if (desc->addresses) +- snprintf(buf, bufsz, "%d", desc->addresses[cpu]); ++ snprintf(buf, bufsz, "%d", desc->addresses[idx]); + break; + case COL_CONFIGURED: + if (!desc->configured) + break; + if (mod->mode == OUTPUT_PARSABLE) +- snprintf(buf, bufsz, +- desc->configured[cpu] ? _("Y") : _("N")); ++ snprintf(buf, bufsz, "%s", ++ desc->configured[idx] ? _("Y") : _("N")); + else +- snprintf(buf, bufsz, +- desc->configured[cpu] ? _("yes") : _("no")); ++ snprintf(buf, bufsz, "%s", ++ desc->configured[idx] ? _("yes") : _("no")); + break; + case COL_ONLINE: + if (!desc->online) + break; + if (mod->mode == OUTPUT_PARSABLE) +- snprintf(buf, bufsz, ++ snprintf(buf, bufsz, "%s", + is_cpu_online(desc, cpu) ? _("Y") : _("N")); + else +- snprintf(buf, bufsz, ++ snprintf(buf, bufsz, "%s", + is_cpu_online(desc, cpu) ? _("yes") : _("no")); + break; ++ case COL_MAXMHZ: ++ if (desc->maxmhz) ++ xstrncpy(buf, desc->maxmhz[idx], bufsz); ++ break; ++ case COL_MINMHZ: ++ if (desc->minmhz) ++ xstrncpy(buf, desc->minmhz[idx], bufsz); ++ break; + } + return buf; + } +@@ -982,14 +1575,16 @@ get_cell_header(struct lscpu_desc *desc, int col, + + for (i = desc->ncaches - 1; i >= 0; i--) { + int x = snprintf(p, sz, "%s", desc->caches[i].name); +- if (x <= 0 || (size_t) x + 2 > sz) ++ if (x < 0 || (size_t) x >= sz) + return NULL; + sz -= x; + p += x; + if (i > 0) { ++ if (sz < 2) ++ return NULL; + *p++ = mod->compat ? ',' : ':'; + *p = '\0'; +- sz++; ++ sz--; + } + } + if (desc->ncaches) +@@ -1073,12 +1668,13 @@ print_parsable(struct lscpu_desc *desc, int cols[], int ncols, + */ + for (i = 0; i < desc->ncpuspos; i++) { + int c; ++ int cpu = real_cpu_num(desc, i); + +- if (!mod->offline && desc->online && !is_cpu_online(desc, i)) ++ if (!mod->offline && desc->online && !is_cpu_online(desc, cpu)) + continue; +- if (!mod->online && desc->online && is_cpu_online(desc, i)) ++ if (!mod->online && desc->online && is_cpu_online(desc, cpu)) + continue; +- if (desc->present && !is_cpu_present(desc, i)) ++ if (desc->present && !is_cpu_present(desc, cpu)) + continue; + for (c = 0; c < ncols; c++) { + if (mod->compat && cols[c] == COL_CACHE) { +@@ -1106,38 +1702,49 @@ print_readable(struct lscpu_desc *desc, int cols[], int ncols, + struct lscpu_modifier *mod) + { + int i; +- char buf[BUFSIZ], *data; +- struct tt *tt = tt_new_table(0); ++ char buf[BUFSIZ]; ++ const char *data; ++ struct libscols_table *table; + +- if (!tt) ++ scols_init_debug(0); ++ ++ table = scols_new_table(); ++ if (!table) + err(EXIT_FAILURE, _("failed to initialize output table")); + + for (i = 0; i < ncols; i++) { + data = get_cell_header(desc, cols[i], mod, buf, sizeof(buf)); +- tt_define_column(tt, xstrdup(data), 0, 0); ++ if (!scols_table_new_column(table, xstrdup(data), 0, 0)) ++ err(EXIT_FAILURE, _("failed to initialize output column")); + } + + for (i = 0; i < desc->ncpuspos; i++) { + int c; +- struct tt_line *line; ++ struct libscols_line *line; ++ int cpu = real_cpu_num(desc, i); + +- if (!mod->offline && desc->online && !is_cpu_online(desc, i)) ++ if (!mod->offline && desc->online && !is_cpu_online(desc, cpu)) + continue; +- if (!mod->online && desc->online && is_cpu_online(desc, i)) ++ if (!mod->online && desc->online && is_cpu_online(desc, cpu)) + continue; +- if (desc->present && !is_cpu_present(desc, i)) ++ if (desc->present && !is_cpu_present(desc, cpu)) + continue; + +- line = tt_add_line(tt, NULL); ++ line = scols_table_new_line(table, NULL); ++ if (!line) ++ err(EXIT_FAILURE, _("failed to initialize output line")); + + for (c = 0; c < ncols; c++) { + data = get_cell_data(desc, i, cols[c], mod, + buf, sizeof(buf)); +- tt_line_set_data(line, c, data && *data ? xstrdup(data) : "-"); ++ if (!data || !*data) ++ data = "-"; ++ scols_line_set_data(line, c, data); + } + } + +- tt_print_table(tt); ++ scols_print_table(table); ++ scols_unref_table(table); + } + + /* output formats "<key> <value>"*/ +@@ -1211,8 +1818,9 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod) + err(EXIT_FAILURE, _("failed to callocate cpu set")); + CPU_ZERO_S(setsize, set); + for (i = 0; i < desc->ncpuspos; i++) { +- if (!is_cpu_online(desc, i) && is_cpu_present(desc, i)) +- CPU_SET_S(i, setsize, set); ++ int cpu = real_cpu_num(desc, i); ++ if (!is_cpu_online(desc, cpu) && is_cpu_present(desc, cpu)) ++ CPU_SET_S(cpu, setsize, set); + } + print_cpuset(mod->hex ? _("Off-line CPU(s) mask:") : + _("Off-line CPU(s) list:"), +@@ -1221,9 +1829,12 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod) + } + + if (desc->nsockets) { +- int cores_per_socket, sockets_per_book, books; ++ int threads_per_core, cores_per_socket, sockets_per_book; ++ int books_per_drawer, drawers; ++ FILE *fd; + +- cores_per_socket = sockets_per_book = books = 0; ++ threads_per_core = cores_per_socket = sockets_per_book = 0; ++ books_per_drawer = drawers = 0; + /* s390 detects its cpu topology via /proc/sysinfo, if present. + * Using simply the cpu topology masks in sysfs will not give + * usable results since everything is virtualized. E.g. +@@ -1232,27 +1843,36 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod) + * If the cpu topology is not exported (e.g. 2nd level guest) + * fall back to old calculation scheme. + */ +- if (path_exist(_PATH_PROC_SYSINFO)) { +- FILE *fd = path_fopen("r", 0, _PATH_PROC_SYSINFO); ++ if ((fd = path_fopen("r", 0, _PATH_PROC_SYSINFO))) { + char pbuf[BUFSIZ]; +- int t0, t1, t2; ++ int t0, t1; + + while (fd && fgets(pbuf, sizeof(pbuf), fd) != NULL) { + if (sscanf(pbuf, "CPU Topology SW:%d%d%d%d%d%d", +- &t0, &t1, &t2, &books, &sockets_per_book, ++ &t0, &t1, &drawers, &books_per_drawer, ++ &sockets_per_book, + &cores_per_socket) == 6) + break; + } + if (fd) + fclose(fd); + } +- print_n(_("Thread(s) per core:"), desc->nthreads / desc->ncores); ++ if (desc->mtid) ++ threads_per_core = atoi(desc->mtid) + 1; ++ print_n(_("Thread(s) per core:"), ++ threads_per_core ?: desc->nthreads / desc->ncores); + print_n(_("Core(s) per socket:"), + cores_per_socket ?: desc->ncores / desc->nsockets); + if (desc->nbooks) { + print_n(_("Socket(s) per book:"), + sockets_per_book ?: desc->nsockets / desc->nbooks); +- print_n(_("Book(s):"), books ?: desc->nbooks); ++ if (desc->ndrawers) { ++ print_n(_("Book(s) per drawer:"), ++ books_per_drawer ?: desc->nbooks / desc->ndrawers); ++ print_n(_("Drawer(s):"), drawers ?: desc->ndrawers); ++ } else { ++ print_n(_("Book(s):"), books_per_drawer ?: desc->nbooks); ++ } + } else { + print_n(_("Socket(s):"), sockets_per_book ?: desc->nsockets); + } +@@ -1261,6 +1881,8 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod) + print_n(_("NUMA node(s):"), desc->nnodes); + if (desc->vendor) + print_s(_("Vendor ID:"), desc->vendor); ++ if (desc->machinetype) ++ print_s(_("Machine type:"), desc->machinetype); + if (desc->family) + print_s(_("CPU family:"), desc->family); + if (desc->model || desc->revision) +@@ -1271,6 +1893,14 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod) + print_s(_("Stepping:"), desc->stepping); + if (desc->mhz) + print_s(_("CPU MHz:"), desc->mhz); ++ if (desc->dynamic_mhz) ++ print_s(_("CPU dynamic MHz:"), desc->dynamic_mhz); ++ if (desc->static_mhz) ++ print_s(_("CPU static MHz:"), desc->static_mhz); ++ if (desc->maxmhz) ++ print_s(_("CPU max MHz:"), desc->maxmhz[0]); ++ if (desc->minmhz) ++ print_s(_("CPU min MHz:"), desc->minmhz[0]); + if (desc->bogomips) + print_s(_("BogoMIPS:"), desc->bogomips); + if (desc->virtflag) { +@@ -1297,10 +1927,29 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod) + } + } + ++ if (desc->necaches) { ++ char cbuf[512]; ++ ++ for (i = desc->necaches - 1; i >= 0; i--) { ++ snprintf(cbuf, sizeof(cbuf), ++ _("%s cache:"), desc->ecaches[i].name); ++ print_s(cbuf, desc->ecaches[i].size); ++ } ++ } ++ + for (i = 0; i < desc->nnodes; i++) { + snprintf(buf, sizeof(buf), _("NUMA node%d CPU(s):"), desc->idx2nodenum[i]); + print_cpuset(buf, desc->nodemaps[i], mod->hex); + } ++ ++ if (desc->flags) ++ print_s(_("Flags:"), desc->flags); ++ ++ if (desc->physsockets) { ++ print_n(_("Physical sockets:"), desc->physsockets); ++ print_n(_("Physical chips:"), desc->physchips); ++ print_n(_("Physical cores/chip:"), desc->physcoresperchip); ++ } + } + + static void __attribute__((__noreturn__)) usage(FILE *out) +@@ -1310,6 +1959,9 @@ static void __attribute__((__noreturn__)) usage(FILE *out) + fputs(USAGE_HEADER, out); + fprintf(out, _(" %s [options]\n"), program_invocation_short_name); + ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Display information about the CPU architecture.\n"), out); ++ + fputs(USAGE_OPTIONS, out); + fputs(_(" -a, --all print both online and offline CPUs (default for -e)\n"), out); + fputs(_(" -b, --online print online CPUs only (default for -p)\n"), out); +@@ -1318,6 +1970,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out) + fputs(_(" -p, --parse[=<list>] print out a parsable format\n"), out); + fputs(_(" -s, --sysroot <dir> use specified directory as system root\n"), out); + fputs(_(" -x, --hex print hexadecimal masks rather than lists of CPUs\n"), out); ++ fputs(_(" -y, --physical print physical instead of logical IDs\n"), out); + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); + fputs(USAGE_VERSION, out); +@@ -1327,7 +1980,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out) + for (i = 0; i < ARRAY_SIZE(coldescs); i++) + fprintf(out, " %13s %s\n", coldescs[i].name, _(coldescs[i].help)); + +- fprintf(out, _("\nFor more details see lscpu(1).\n")); ++ fprintf(out, USAGE_MAN_TAIL("lscpu(1)")); + + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); + } +@@ -1335,22 +1988,23 @@ static void __attribute__((__noreturn__)) usage(FILE *out) + int main(int argc, char *argv[]) + { + struct lscpu_modifier _mod = { .mode = OUTPUT_SUMMARY }, *mod = &_mod; +- struct lscpu_desc _desc = { .flags = 0 }, *desc = &_desc; ++ struct lscpu_desc _desc = { .flags = NULL }, *desc = &_desc; + int c, i; + int columns[ARRAY_SIZE(coldescs)], ncolumns = 0; + int cpu_modifier_specified = 0; + + static const struct option longopts[] = { +- { "all", no_argument, 0, 'a' }, +- { "online", no_argument, 0, 'b' }, +- { "offline", no_argument, 0, 'c' }, +- { "help", no_argument, 0, 'h' }, +- { "extended", optional_argument, 0, 'e' }, +- { "parse", optional_argument, 0, 'p' }, +- { "sysroot", required_argument, 0, 's' }, +- { "hex", no_argument, 0, 'x' }, +- { "version", no_argument, 0, 'V' }, +- { NULL, 0, 0, 0 } ++ { "all", no_argument, NULL, 'a' }, ++ { "online", no_argument, NULL, 'b' }, ++ { "offline", no_argument, NULL, 'c' }, ++ { "help", no_argument, NULL, 'h' }, ++ { "extended", optional_argument, NULL, 'e' }, ++ { "parse", optional_argument, NULL, 'p' }, ++ { "sysroot", required_argument, NULL, 's' }, ++ { "physical", no_argument, NULL, 'y' }, ++ { "hex", no_argument, NULL, 'x' }, ++ { "version", no_argument, NULL, 'V' }, ++ { NULL, 0, NULL, 0 } + }; + + static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ +@@ -1365,7 +2019,7 @@ int main(int argc, char *argv[]) + textdomain(PACKAGE); + atexit(close_stdout); + +- while ((c = getopt_long(argc, argv, "abce::hp::s:xV", longopts, NULL)) != -1) { ++ while ((c = getopt_long(argc, argv, "abce::hp::s:xyV", longopts, NULL)) != -1) { + + err_exclusive_options(c, longopts, excl, excl_st); + +@@ -1404,12 +2058,14 @@ int main(int argc, char *argv[]) + case 'x': + mod->hex = 1; + break; ++ case 'y': ++ mod->physical = 1; ++ break; + case 'V': +- printf(_("%s from %s\n"), program_invocation_short_name, +- PACKAGE_STRING); ++ printf(UTIL_LINUX_VERSION); + return EXIT_SUCCESS; + default: +- usage(stderr); ++ errtryhelp(EXIT_FAILURE); + } + } + +@@ -1433,17 +2089,27 @@ int main(int argc, char *argv[]) + read_basicinfo(desc, mod); + + for (i = 0; i < desc->ncpuspos; i++) { ++ /* only consider present CPUs */ ++ if (desc->present && ++ !CPU_ISSET(real_cpu_num(desc, i), desc->present)) ++ continue; + read_topology(desc, i); + read_cache(desc, i); + read_polarization(desc, i); + read_address(desc, i); + read_configured(desc, i); ++ read_max_mhz(desc, i); ++ read_min_mhz(desc, i); + } + + if (desc->caches) + qsort(desc->caches, desc->ncaches, + sizeof(struct cpu_cache), cachecmp); + ++ if (desc->ecaches) ++ qsort(desc->ecaches, desc->necaches, ++ sizeof(struct cpu_cache), cachecmp); ++ + read_nodes(desc); + read_hypervisor(desc, mod); + +@@ -1468,6 +2134,8 @@ int main(int argc, char *argv[]) + columns[ncolumns++] = COL_CPU; + if (desc->nodemaps) + columns[ncolumns++] = COL_NODE; ++ if (desc->drawermaps) ++ columns[ncolumns++] = COL_DRAWER; + if (desc->bookmaps) + columns[ncolumns++] = COL_BOOK; + if (desc->socketmaps) +@@ -1484,6 +2152,10 @@ int main(int argc, char *argv[]) + columns[ncolumns++] = COL_POLARIZATION; + if (desc->addresses) + columns[ncolumns++] = COL_ADDRESS; ++ if (desc->maxmhz) ++ columns[ncolumns++] = COL_MAXMHZ; ++ if (desc->minmhz) ++ columns[ncolumns++] = COL_MINMHZ; + } + print_readable(desc, columns, ncolumns, mod); + break; +diff --git a/sys-utils/lscpu.h b/sys-utils/lscpu.h +new file mode 100644 +index 0000000..4906c26 +--- /dev/null ++++ b/sys-utils/lscpu.h +@@ -0,0 +1,26 @@ ++#ifndef LSCPU_H ++#define LSCPU_H ++ ++/* hypervisor vendors */ ++enum { ++ HYPER_NONE = 0, ++ HYPER_XEN, ++ HYPER_KVM, ++ HYPER_MSHV, ++ HYPER_VMWARE, ++ HYPER_IBM, /* sys-z powervm */ ++ HYPER_VSERVER, ++ HYPER_UML, ++ HYPER_INNOTEK, /* VBOX */ ++ HYPER_HITACHI, ++ HYPER_PARALLELS, /* OpenVZ/VIrtuozzo */ ++ HYPER_VBOX, ++ HYPER_OS400, ++ HYPER_PHYP, ++ HYPER_SPAR, ++ HYPER_WSL, ++}; ++ ++extern int read_hypervisor_dmi(void); ++ ++#endif /* LSCPU_H */ +-- +2.9.3 + diff --git a/SOURCES/0116-fdisk-use-sysfs_devno_is_wholedisk.patch b/SOURCES/0116-fdisk-use-sysfs_devno_is_wholedisk.patch new file mode 100644 index 0000000..e758236 --- /dev/null +++ b/SOURCES/0116-fdisk-use-sysfs_devno_is_wholedisk.patch @@ -0,0 +1,58 @@ +From e86fe103accdf5dd688b3710c873094cfa41ae5f Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 21 Mar 2017 15:11:29 +0100 +Subject: [PATCH 116/116] fdisk: use sysfs_devno_is_wholedisk() + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1402183 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + lib/wholedisk.c | 23 ++++++++--------------- + 1 file changed, 8 insertions(+), 15 deletions(-) + +diff --git a/lib/wholedisk.c b/lib/wholedisk.c +index 5161a1e..7c63204 100644 +--- a/lib/wholedisk.c ++++ b/lib/wholedisk.c +@@ -1,14 +1,10 @@ +-/* +- * No copyright is claimed. This code is in the public domain; do with +- * it what you wish. +- * +- * Written by Karel Zak <kzak@redhat.com> +- */ ++ + #include <stdio.h> + #include <stdlib.h> + #include <ctype.h> + + #include "blkdev.h" ++#include "sysfs.h" + #include "wholedisk.h" + + int is_whole_disk_fd(int fd, const char *name) +@@ -35,16 +31,13 @@ int is_whole_disk_fd(int fd, const char *name) + + int is_whole_disk(const char *name) + { +- int fd = -1, res = 0; +-#ifdef HDIO_GETGEO +- fd = open(name, O_RDONLY|O_CLOEXEC); +- if (fd != -1) +-#endif +- res = is_whole_disk_fd(fd, name); ++ dev_t devno = sysfs_devname_to_devno(name, NULL); ++ ++ if (sysfs_devno_is_lvm_private(devno) || ++ sysfs_devno_is_wholedisk(devno) <= 0) ++ return 0; + +- if (fd != -1) +- close(fd); +- return res; ++ return 1; + } + + #ifdef TEST_PROGRAM +-- +2.9.3 + diff --git a/SOURCES/0117-zramctl-add-bash-completion.patch b/SOURCES/0117-zramctl-add-bash-completion.patch new file mode 100644 index 0000000..22f54f3 --- /dev/null +++ b/SOURCES/0117-zramctl-add-bash-completion.patch @@ -0,0 +1,93 @@ +From 0b8ef4d8289fa1af0296ae01faf0e60293c725fd Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 22 Mar 2017 12:13:43 +0100 +Subject: [PATCH] zramctl: add bash completion + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1358755 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + bash-completion/Makemodule.am | 3 ++- + bash-completion/zramctl | 57 +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 59 insertions(+), 1 deletion(-) + create mode 100644 bash-completion/zramctl + +diff --git a/bash-completion/Makemodule.am b/bash-completion/Makemodule.am +index c3791e7..84ab258 100644 +--- a/bash-completion/Makemodule.am ++++ b/bash-completion/Makemodule.am +@@ -30,7 +30,8 @@ dist_bashcompletion_DATA = \ + bash-completion/setsid \ + bash-completion/tailf \ + bash-completion/whereis \ +- bash-completion/wipefs ++ bash-completion/wipefs \ ++ bash-completion/zramctl + + # disk-utils... + if BUILD_BFS +diff --git a/bash-completion/zramctl b/bash-completion/zramctl +new file mode 100644 +index 0000000..a4ef536 +--- /dev/null ++++ b/bash-completion/zramctl +@@ -0,0 +1,57 @@ ++_zramctl_module() ++{ ++ local cur prev OPTS ++ COMPREPLY=() ++ cur="${COMP_WORDS[COMP_CWORD]}" ++ prev="${COMP_WORDS[COMP_CWORD-1]}" ++ case $prev in ++ '-a'|'--algorithm') ++ COMPREPLY=( $(compgen -W "lzo lz4" -- $cur) ) ++ return 0 ++ ;; ++ '-o'|'--output') ++ local prefix realcur OUTPUT_ALL OUTPUT ++ realcur="${cur##*,}" ++ prefix="${cur%$realcur}" ++ OUTPUT_ALL="NAME DISKSIZE DATA COMPR ALGORITHM STREAMS ZERO-PAGES TOTAL MOUNTPOINT" ++ for WORD in $OUTPUT_ALL; do ++ if ! [[ $prefix == *"$WORD"* ]]; then ++ OUTPUT="$WORD $OUTPUT" ++ fi ++ done ++ compopt -o nospace ++ COMPREPLY=( $(compgen -P "$prefix" -W "$OUTPUT" -S ',' -- $realcur) ) ++ return 0 ++ ;; ++ '-s'|'--size') ++ COMPREPLY=( $(compgen -W "size" -- $cur) ) ++ return 0 ++ ;; ++ '-t'|'--streams') ++ COMPREPLY=( $(compgen -W "number" -- $cur) ) ++ return 0 ++ ;; ++ esac ++ case $cur in ++ -*) ++ OPTS=" --algorithm ++ --bytes ++ --find ++ --noheadings ++ --output ++ --raw ++ --reset ++ --size ++ --streams ++ --help ++ --version" ++ COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) ++ return 0 ++ ;; ++ esac ++ local IFS=$'\n' ++ compopt -o filenames ++ COMPREPLY=( $(compgen -f -- ${cur:-"/dev/zram"}) ) ++ return 0 ++} ++complete -F _zramctl_module zramctl +-- +2.9.3 + diff --git a/SOURCES/0118-zramctl-make-mm_stat-parser-more-robust.patch b/SOURCES/0118-zramctl-make-mm_stat-parser-more-robust.patch new file mode 100644 index 0000000..9e5c205 --- /dev/null +++ b/SOURCES/0118-zramctl-make-mm_stat-parser-more-robust.patch @@ -0,0 +1,38 @@ +From a7f11e525e9dd5abf844ada0ddd0ae74950e2e40 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 30 Mar 2017 12:10:01 +0200 +Subject: [PATCH] zramctl: make mm_stat parser more robust + +Let's fallback to attribute files if mm_stat file is incomplete. It +should not happen, but I have seen RHEL7 kernel where is no +num_migrated/pages_compacted attribute... + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1358755 +Upstream: http://github.com/karelzak/util-linux/commit/2546d54bd8b0ceac75d6d7e6c483479022d97509 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/zramctl.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/sys-utils/zramctl.c b/sys-utils/zramctl.c +index 853401c..c3112d6 100644 +--- a/sys-utils/zramctl.c ++++ b/sys-utils/zramctl.c +@@ -359,8 +359,12 @@ static char *get_mm_stat(struct zram *z, size_t idx, int bytes) + str = sysfs_strdup(sysfs, "mm_stat"); + if (str) { + z->mm_stat = strv_split(str, " "); +- if (strv_length(z->mm_stat) < ARRAY_SIZE(mm_stat_names)) +- errx(EXIT_FAILURE, _("Failed to parse mm_stat")); ++ ++ /* make sure kernel provides mm_stat as expected */ ++ if (strv_length(z->mm_stat) < ARRAY_SIZE(mm_stat_names)) { ++ strv_free(z->mm_stat); ++ z->mm_stat = NULL; ++ } + } + z->mm_stat_probed = 1; + free(str); +-- +2.9.3 + diff --git a/SOURCES/0119-fdisk-improve-menu-and-u-for-GPT.patch b/SOURCES/0119-fdisk-improve-menu-and-u-for-GPT.patch new file mode 100644 index 0000000..2d8099a --- /dev/null +++ b/SOURCES/0119-fdisk-improve-menu-and-u-for-GPT.patch @@ -0,0 +1,74 @@ +From 846f494a89cfe00bcea5e12d9526df76be9196a6 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 4 Apr 2017 11:12:29 +0200 +Subject: [PATCH] fdisk: improve menu and 'u' for GPT + +* print 't' in the menu for GPT +* don't toggle to cylinders for GPT +* force sectors if GPT detected +* improve expert 'd' command description + +Addresses: Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1344720 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + fdisks/fdisk.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/fdisks/fdisk.c b/fdisks/fdisk.c +index 177921a..b47b975 100644 +--- a/fdisks/fdisk.c ++++ b/fdisks/fdisk.c +@@ -70,7 +70,7 @@ static const struct menulist_descr menulist[] = { + {'c', N_("toggle the dos compatibility flag"), {FDISK_DISKLABEL_DOS, 0}}, + {'c', N_("toggle the mountable flag"), {FDISK_DISKLABEL_SUN, 0}}, + {'d', N_("delete a partition"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF | FDISK_DISKLABEL_GPT, 0}}, +- {'d', N_("print the raw data in the partition table"), {0, FDISK_DISKLABEL_ANY}}, ++ {'d', N_("print the raw data in the first sector"), {0, FDISK_DISKLABEL_ANY}}, + {'e', N_("change number of extra sectors per cylinder"), {0, FDISK_DISKLABEL_SUN}}, + {'e', N_("edit drive data"), {FDISK_DISKLABEL_OSF, 0}}, + {'e', N_("list extended partitions"), {0, FDISK_DISKLABEL_DOS}}, +@@ -94,7 +94,7 @@ static const struct menulist_descr menulist[] = { + {'s', N_("change number of sectors/track"), {0, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN}}, + {'s', N_("create a new empty Sun disklabel"), {~FDISK_DISKLABEL_OSF, 0}}, + {'s', N_("show complete disklabel"), {FDISK_DISKLABEL_OSF, 0}}, +- {'t', N_("change a partition's system id"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF, 0}}, ++ {'t', N_("change a partition's system id"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF | FDISK_DISKLABEL_GPT, 0}}, + {'u', N_("change display/entry units"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF, 0}}, + {'v', N_("verify the partition table"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_GPT, + FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_GPT}}, +@@ -1018,6 +1018,11 @@ static void command_prompt(struct fdisk_context *cxt) + fdisk_context_switch_label(cxt, "dos"); + } + ++ if (fdisk_is_disklabel(cxt, GPT) && fdisk_context_use_cylinders(cxt)) { ++ printf(_("Use cylinders for GPT is unsupported. ")); ++ toggle_units(cxt); ++ } ++ + while (1) { + + assert(cxt->label); +@@ -1073,6 +1078,8 @@ static void command_prompt(struct fdisk_context *cxt) + break; + case 'g': + fdisk_create_disklabel(cxt, "gpt"); ++ if (fdisk_is_disklabel(cxt, GPT) && fdisk_context_use_cylinders(cxt)) ++ toggle_units(cxt); + break; + case 'G': + fdisk_create_disklabel(cxt, "sgi"); +@@ -1107,7 +1114,10 @@ static void command_prompt(struct fdisk_context *cxt) + change_partition_type(cxt); + break; + case 'u': +- toggle_units(cxt); ++ if (fdisk_is_disklabel(cxt, GPT) && !fdisk_context_use_cylinders(cxt)) ++ printf(_("Use cylinders for GPT is unsupported.")); ++ else ++ toggle_units(cxt); + break; + case 'v': + verify(cxt); +-- +2.9.3 + diff --git a/SOURCES/0120-tests-update-for-RHEL7.4-changes.patch b/SOURCES/0120-tests-update-for-RHEL7.4-changes.patch new file mode 100644 index 0000000..6e21777 --- /dev/null +++ b/SOURCES/0120-tests-update-for-RHEL7.4-changes.patch @@ -0,0 +1,209 @@ +From e5d31b4ffb3f978a8935d35301a59eeafe6a50d4 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 4 Apr 2017 11:58:09 +0200 +Subject: [PATCH 120/121] tests: update for RHEL7.4 changes + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1360764 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1344726 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1402183 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + lib/Makemodule.am | 1 + + tests/expected/fdisk/gpt | 5 +++++ + tests/expected/lscpu/lscpu-armv7 | 5 +++++ + tests/expected/lscpu/lscpu-s390-kvm | 2 ++ + tests/expected/lscpu/lscpu-s390-lpar | 2 ++ + tests/expected/lscpu/lscpu-s390-zvm | 2 ++ + tests/expected/lscpu/lscpu-x86_64-64cpu | 5 ++++- + tests/expected/lscpu/lscpu-x86_64-dell_e4310 | 5 ++++- + tests/ts/fdisk/gpt | 3 ++- + 9 files changed, 27 insertions(+), 3 deletions(-) + +diff --git a/lib/Makemodule.am b/lib/Makemodule.am +index faf9d74..acae27a 100644 +--- a/lib/Makemodule.am ++++ b/lib/Makemodule.am +@@ -82,6 +82,7 @@ test_ismounted_LDADD = libcommon.la + + test_wholedisk_SOURCES = lib/wholedisk.c + test_wholedisk_CFLAGS = -DTEST_PROGRAM ++test_wholedisk_LDADD = libcommon.la + + test_mangle_SOURCES = lib/mangle.c + test_mangle_CFLAGS = -DTEST_PROGRAM +diff --git a/tests/expected/fdisk/gpt b/tests/expected/fdisk/gpt +index b73d5c3..017d819 100644 +--- a/tests/expected/fdisk/gpt ++++ b/tests/expected/fdisk/gpt +@@ -7,6 +7,7 @@ Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 512 bytes + I/O size (minimum/optimal): 512 bytes / 512 bytes + Disk label type: gpt ++Disk identifier: <removed> + + + # Start End Size Type Name +@@ -21,6 +22,7 @@ Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 512 bytes + I/O size (minimum/optimal): 512 bytes / 512 bytes + Disk label type: gpt ++Disk identifier: <removed> + + + # Start End Size Type Name +@@ -42,6 +44,7 @@ Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 512 bytes + I/O size (minimum/optimal): 512 bytes / 512 bytes + Disk label type: gpt ++Disk identifier: <removed> + + + # Start End Size Type Name +@@ -63,6 +66,7 @@ Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 512 bytes + I/O size (minimum/optimal): 512 bytes / 512 bytes + Disk label type: gpt ++Disk identifier: <removed> + + + # Start End Size Type Name +@@ -83,6 +87,7 @@ Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 512 bytes + I/O size (minimum/optimal): 512 bytes / 512 bytes + Disk label type: gpt ++Disk identifier: <removed> + + + # Start End Size Type Name +diff --git a/tests/expected/lscpu/lscpu-armv7 b/tests/expected/lscpu/lscpu-armv7 +index a1b691c..bcb16cc 100644 +--- a/tests/expected/lscpu/lscpu-armv7 ++++ b/tests/expected/lscpu/lscpu-armv7 +@@ -3,6 +3,11 @@ On-line CPU(s) list: 0,1 + Thread(s) per core: 1 + Core(s) per socket: 2 + Socket(s): 1 ++Model: 4 ++CPU max MHz: 1700.0000 ++CPU min MHz: 200.0000 ++BogoMIPS: 1694.10 ++Flags: swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt + + # The following is the parsable format, which can be fed to other + # programs. Each different item in every column has an unique ID +diff --git a/tests/expected/lscpu/lscpu-s390-kvm b/tests/expected/lscpu/lscpu-s390-kvm +index 1aa42f9..66d1c04 100644 +--- a/tests/expected/lscpu/lscpu-s390-kvm ++++ b/tests/expected/lscpu/lscpu-s390-kvm +@@ -6,11 +6,13 @@ Core(s) per socket: 1 + Socket(s) per book: 1 + Book(s): 3 + Vendor ID: IBM/S390 ++Machine type: 2817 + BogoMIPS: 14367.00 + Hypervisor: KVM/Linux + Hypervisor vendor: KVM + Virtualization type: full + Dispatching mode: horizontal ++Flags: esan3 zarch stfle msa ldisp eimm dfp edat etf3eh highgprs + + # The following is the parsable format, which can be fed to other + # programs. Each different item in every column has an unique ID +diff --git a/tests/expected/lscpu/lscpu-s390-lpar b/tests/expected/lscpu/lscpu-s390-lpar +index 0799ab9..9c8ac2c 100644 +--- a/tests/expected/lscpu/lscpu-s390-lpar ++++ b/tests/expected/lscpu/lscpu-s390-lpar +@@ -7,11 +7,13 @@ Core(s) per socket: 4 + Socket(s) per book: 6 + Book(s): 4 + Vendor ID: IBM/S390 ++Machine type: 2817 + BogoMIPS: 14367.00 + Hypervisor: PR/SM + Hypervisor vendor: IBM + Virtualization type: full + Dispatching mode: vertical ++Flags: esan3 zarch stfle msa ldisp eimm dfp etf3eh highgprs + + # The following is the parsable format, which can be fed to other + # programs. Each different item in every column has an unique ID +diff --git a/tests/expected/lscpu/lscpu-s390-zvm b/tests/expected/lscpu/lscpu-s390-zvm +index 04dcf76..4cd6b8f 100644 +--- a/tests/expected/lscpu/lscpu-s390-zvm ++++ b/tests/expected/lscpu/lscpu-s390-zvm +@@ -6,11 +6,13 @@ Core(s) per socket: 1 + Socket(s) per book: 1 + Book(s): 4 + Vendor ID: IBM/S390 ++Machine type: 2817 + BogoMIPS: 14367.00 + Hypervisor: z/VM 6.1.0 + Hypervisor vendor: IBM + Virtualization type: full + Dispatching mode: horizontal ++Flags: esan3 zarch stfle msa ldisp eimm dfp etf3eh highgprs + + # The following is the parsable format, which can be fed to other + # programs. Each different item in every column has an unique ID +diff --git a/tests/expected/lscpu/lscpu-x86_64-64cpu b/tests/expected/lscpu/lscpu-x86_64-64cpu +index 32aa57c..07990ea 100644 +--- a/tests/expected/lscpu/lscpu-x86_64-64cpu ++++ b/tests/expected/lscpu/lscpu-x86_64-64cpu +@@ -11,7 +11,9 @@ Model: 46 + Model name: Intel(R) Xeon(R) CPU X7550 @ 2.00GHz + Stepping: 6 + CPU MHz: 1064.000 +-BogoMIPS: 3989.44 ++CPU max MHz: 1996.0000 ++CPU min MHz: 1064.0000 ++BogoMIPS: 3990.31 + Virtualization: VT-x + L1d cache: 32K + L1i cache: 32K +@@ -20,6 +22,7 @@ L3 cache: 18432K + NUMA node0 CPU(s): 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62 + NUMA node2 CPU(s): 1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61 + NUMA node3 CPU(s): 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63 ++Flags: fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 x2apic popcnt lahf_lm ida epb dts tpr_shadow vnmi flexpriority ept vpid + + # The following is the parsable format, which can be fed to other + # programs. Each different item in every column has an unique ID +diff --git a/tests/expected/lscpu/lscpu-x86_64-dell_e4310 b/tests/expected/lscpu/lscpu-x86_64-dell_e4310 +index a81878d..39ec32c 100644 +--- a/tests/expected/lscpu/lscpu-x86_64-dell_e4310 ++++ b/tests/expected/lscpu/lscpu-x86_64-dell_e4310 +@@ -11,13 +11,16 @@ Model: 37 + Model name: Intel(R) Core(TM) i5 CPU M 560 @ 2.67GHz + Stepping: 5 + CPU MHz: 1199.000 +-BogoMIPS: 5319.97 ++CPU max MHz: 2667.0000 ++CPU min MHz: 1199.0000 ++BogoMIPS: 5319.92 + Virtualization: VT-x + L1d cache: 32K + L1i cache: 32K + L2 cache: 256K + L3 cache: 3072K + NUMA node0 CPU(s): 0-3 ++Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt aes lahf_lm ida arat dts tpr_shadow vnmi flexpriority ept vpid + + # The following is the parsable format, which can be fed to other + # programs. Each different item in every column has an unique ID +diff --git a/tests/ts/fdisk/gpt b/tests/ts/fdisk/gpt +index a0902ca..ec3438f 100755 +--- a/tests/ts/fdisk/gpt ++++ b/tests/ts/fdisk/gpt +@@ -38,7 +38,8 @@ function print_layout { + echo -ne "\n---layout----------" >> $TS_OUTPUT + $TS_CMD_FDISK -l ${TEST_IMAGE_NAME} 2> /dev/null | \ + sed 's/^.*\.img/__ts_dev__/g; +- s/^[[:blank:]]*Device Boot/ Device Boot/g' >> $TS_OUTPUT 2>&1 ++ s/^[[:blank:]]*Device Boot/ Device Boot/g; ++ s/^Disk identifier:.*/Disk identifier: <removed>/g' >> $TS_OUTPUT 2>&1 + echo -ne "-------------------\n\n" >> $TS_OUTPUT + } + +-- +2.9.3 + diff --git a/SOURCES/0121-zramctl-be-more-specific-about-default-output.patch b/SOURCES/0121-zramctl-be-more-specific-about-default-output.patch new file mode 100644 index 0000000..105761a --- /dev/null +++ b/SOURCES/0121-zramctl-be-more-specific-about-default-output.patch @@ -0,0 +1,31 @@ +From 46d4d1c8d1b62c8f2dda1639e75b5d0e6bafaf27 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 4 Apr 2017 12:06:45 +0200 +Subject: [PATCH 121/121] zramctl: be more specific about default output + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1358755 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/zramctl.8 | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/sys-utils/zramctl.8 b/sys-utils/zramctl.8 +index f6fc45c..9ca2983 100644 +--- a/sys-utils/zramctl.8 ++++ b/sys-utils/zramctl.8 +@@ -39,9 +39,9 @@ Set up a zram device: + .SH DESCRIPTION + .B zramctl + is used to quickly set up zram device parameters, to reset zram devices, and to +-query the status of used zram devices. If no option is given, all zram devices +-are shown. +- ++query the status of used zram devices. ++.PP ++If no option is given, all non-zero size zram devices are shown. + .SH OPTIONS + .TP + .BR \-a , " \-\-algorithm lzo" | lz4 +-- +2.9.3 + diff --git a/SOURCES/0122-libfdisk-gpt-fix-UUID-printing.patch b/SOURCES/0122-libfdisk-gpt-fix-UUID-printing.patch new file mode 100644 index 0000000..eefa83f --- /dev/null +++ b/SOURCES/0122-libfdisk-gpt-fix-UUID-printing.patch @@ -0,0 +1,46 @@ +From a95f7a89ed81fb3d7c3135baae20b056b7f8e661 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 10 May 2017 15:26:55 +0200 +Subject: [PATCH] libfdisk: (gpt) fix UUID printing + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1344726 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libfdisk/src/gpt.c | 14 +++----------- + 1 file changed, 3 insertions(+), 11 deletions(-) + +diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c +index 899e1b2..612ce09 100644 +--- a/libfdisk/src/gpt.c ++++ b/libfdisk/src/gpt.c +@@ -1690,7 +1690,7 @@ static int gpt_create_disklabel(struct fdisk_context *cxt) + { + int rc = 0; + ssize_t esz = 0; +- struct gpt_guid *uid; ++ char str[37]; + struct fdisk_gpt_label *gpt; + + assert(cxt); +@@ -1746,16 +1746,8 @@ static int gpt_create_disklabel(struct fdisk_context *cxt) + cxt->label->nparts_max = le32_to_cpu(gpt->pheader->npartition_entries); + cxt->label->nparts_cur = 0; + +- uid = &gpt->pheader->disk_guid; +- fdisk_info(cxt, _("Building a new GPT disklabel " +- "(GUID: %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X)\n"), +- uid->time_low, uid->time_mid, +- uid->time_hi_and_version, +- uid->clock_seq_hi, +- uid->clock_seq_low, +- uid->node[0], uid->node[1], +- uid->node[2], uid->node[3], +- uid->node[4], uid->node[5]); ++ guid_to_string(&gpt->pheader->disk_guid, str); ++ fdisk_info(cxt, _("Building a new GPT disklabel (GUID: %s)\n"), str); + fdisk_label_set_changed(cxt->label, 1); + done: + return rc; +-- +2.9.3 + diff --git a/SOURCES/0123-libblkid-Add-metadata-signature-check-for-IMSM-on-4K.patch b/SOURCES/0123-libblkid-Add-metadata-signature-check-for-IMSM-on-4K.patch new file mode 100644 index 0000000..b17f426 --- /dev/null +++ b/SOURCES/0123-libblkid-Add-metadata-signature-check-for-IMSM-on-4K.patch @@ -0,0 +1,60 @@ +From 2181ce4a5726c4c72e68e6964b7ef7442e507707 Mon Sep 17 00:00:00 2001 +From: Alexey Obitotskiy <aleksey.obitotskiy@intel.com> +Date: Fri, 24 Jun 2016 11:59:35 +0200 +Subject: [PATCH] libblkid: Add metadata signature check for IMSM on 4Kn drives + +Drives with 512 and 4K sectors have different offset for +metadata signature. Without signature detected on 4Kn drives +those drives will not be recognized as raid member. This +patch adds checking for IMSM signature for 4Kn drives. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1451704 +Signed-off-by: Alexey Obitotskiy <aleksey.obitotskiy@intel.com> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/superblocks/isw_raid.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/libblkid/src/superblocks/isw_raid.c b/libblkid/src/superblocks/isw_raid.c +index 065c2b2..81d53a1 100644 +--- a/libblkid/src/superblocks/isw_raid.c ++++ b/libblkid/src/superblocks/isw_raid.c +@@ -25,11 +25,11 @@ struct isw_metadata { + + #define ISW_SIGNATURE "Intel Raid ISM Cfg Sig. " + +- + static int probe_iswraid(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) + { + uint64_t off; ++ unsigned int sector_size; + struct isw_metadata *isw; + + if (pr->size < 0x10000) +@@ -37,16 +37,17 @@ static int probe_iswraid(blkid_probe pr, + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return 1; + +- off = ((pr->size / 0x200) - 2) * 0x200; +- isw = (struct isw_metadata *) +- blkid_probe_get_buffer(pr, +- off, +- sizeof(struct isw_metadata)); ++ sector_size = blkid_probe_get_sectorsize(pr); ++ off = ((pr->size / sector_size) - 2) * sector_size; ++ ++ isw = (struct isw_metadata *)blkid_probe_get_buffer(pr, ++ off, sizeof(struct isw_metadata)); + if (!isw) + return errno ? -errno : 1; + + if (memcmp(isw->sig, ISW_SIGNATURE, sizeof(ISW_SIGNATURE)-1) != 0) + return 1; ++ + if (blkid_probe_sprintf_version(pr, "%6s", + &isw->sig[sizeof(ISW_SIGNATURE)-1]) != 0) + return 1; +-- +2.9.4 + diff --git a/SOURCES/0124-lscpu-use-sysfs-for-table-access-if-available.patch b/SOURCES/0124-lscpu-use-sysfs-for-table-access-if-available.patch new file mode 100644 index 0000000..d31a3ca --- /dev/null +++ b/SOURCES/0124-lscpu-use-sysfs-for-table-access-if-available.patch @@ -0,0 +1,66 @@ +From 6999f3f3ca525bb6b132f4ed804e7f8fe62e5f79 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel <ard.biesheuvel@linaro.org> +Date: Wed, 12 Apr 2017 10:11:29 +0100 +Subject: [PATCH] lscpu: use sysfs for table access if available + +On ARM systems, accessing SMBIOS tables via /dev/mem using read() +calls is not supported. The reason is that such tables are usually +located in EFI_RUNTIME_SERVICE_DATA memory, which is not covered +by the linear mapping on those systems, and so read() calls will +fail. + +So instead, use the /sys/firmware/dmi/tables/DMI sysfs file, which +contains the entire structure table array, and will be available +on any recent Linux system, even on ones that only export the rev3 +SMBIOS entry point, which is currently ignored by lscpu. + +Note that the max 'num' value is inferred from the size. This is not +a limitation of the sysfs interface, but a limitation of the rev3 +entry point, which no longer carries a number of array elements. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1455664 +Upstream: http://github.com/karelzak/util-linux/commit/92a6392c41c11bcb49af9f129dfbd1fed651f044 +Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> +Tested-by: Alexander Graf <agraf@suse.de> +Reviewed-by: Alexander Graf <agraf@suse.de> +--- + sys-utils/lscpu-dmi.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/sys-utils/lscpu-dmi.c b/sys-utils/lscpu-dmi.c +index 0e497d1..a8298ff 100644 +--- a/sys-utils/lscpu-dmi.c ++++ b/sys-utils/lscpu-dmi.c +@@ -192,6 +192,18 @@ static int hypervisor_decode_smbios(uint8_t *buf, const char *devmem) + devmem); + } + ++static int hypervisor_decode_sysfw(void) ++{ ++ static char const sys_fw_dmi_tables[] = "/sys/firmware/dmi/tables/DMI"; ++ struct stat st; ++ ++ if (stat(sys_fw_dmi_tables, &st)) ++ return -1; ++ ++ return hypervisor_from_dmi_table(0, st.st_size, st.st_size / 4, ++ sys_fw_dmi_tables); ++} ++ + /* + * Probe for EFI interface + */ +@@ -242,6 +254,10 @@ int read_hypervisor_dmi(void) + || '\0' != 0) + return rc; + ++ rc = hypervisor_decode_sysfw(); ++ if (rc >= 0) ++ return rc; ++ + /* First try EFI (ia64, Intel-based Mac) */ + switch (address_from_efi(&fp)) { + case EFI_NOT_FOUND: +-- +2.9.4 + diff --git a/SOURCES/0125-lscpu-improve-for-offline-CPUs-on-AMD.patch b/SOURCES/0125-lscpu-improve-for-offline-CPUs-on-AMD.patch new file mode 100644 index 0000000..a6a214c --- /dev/null +++ b/SOURCES/0125-lscpu-improve-for-offline-CPUs-on-AMD.patch @@ -0,0 +1,44 @@ +From 4150eb1025c059f9459f98ef6c6c3fac730eaf93 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 1 Jun 2017 12:07:41 +0200 +Subject: [PATCH] lscpu: improve for offline CPUs on AMD + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1457744 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/lscpu.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c +index 683fd66..1ee73f3 100644 +--- a/sys-utils/lscpu.c ++++ b/sys-utils/lscpu.c +@@ -1550,11 +1550,11 @@ get_cell_data(struct lscpu_desc *desc, int idx, int col, + is_cpu_online(desc, cpu) ? _("yes") : _("no")); + break; + case COL_MAXMHZ: +- if (desc->maxmhz) ++ if (desc->maxmhz && desc->maxmhz[idx]) + xstrncpy(buf, desc->maxmhz[idx], bufsz); + break; + case COL_MINMHZ: +- if (desc->minmhz) ++ if (desc->minmhz && desc->minmhz[idx]) + xstrncpy(buf, desc->minmhz[idx], bufsz); + break; + } +@@ -1897,9 +1897,9 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod) + print_s(_("CPU dynamic MHz:"), desc->dynamic_mhz); + if (desc->static_mhz) + print_s(_("CPU static MHz:"), desc->static_mhz); +- if (desc->maxmhz) ++ if (desc->maxmhz && desc->maxmhz[0]) + print_s(_("CPU max MHz:"), desc->maxmhz[0]); +- if (desc->minmhz) ++ if (desc->minmhz && desc->minmhz[0]) + print_s(_("CPU min MHz:"), desc->minmhz[0]); + if (desc->bogomips) + print_s(_("BogoMIPS:"), desc->bogomips); +-- +2.9.4 + diff --git a/SOURCES/0126-libmount-use-eacess-rather-than-open-to-check-mtab-u.patch b/SOURCES/0126-libmount-use-eacess-rather-than-open-to-check-mtab-u.patch new file mode 100644 index 0000000..60e8fa4 --- /dev/null +++ b/SOURCES/0126-libmount-use-eacess-rather-than-open-to-check-mtab-u.patch @@ -0,0 +1,68 @@ +From 04cad06bed055a5dd373b2f5babc8000a76597a6 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 9 Oct 2017 12:44:48 +0200 +Subject: [PATCH] libmount: use eacess() rather than open() to check mtab/utab + +The open() syscall is probably the most strong way how to check write +accessibility in all situations, but it's overkill and on some +paranoid systems with enabled audit/selinux. It fills logs with +"Permission denied" entries. Let's use eaccess() if available. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1499760 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + configure.ac | 1 + + libmount/src/utils.c | 19 +++++++++++++------ + 2 files changed, 14 insertions(+), 6 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 78258d677..96c5838cf 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -315,6 +315,7 @@ AC_CHECK_FUNCS([ \ + __fpending \ + secure_getenv \ + __secure_getenv \ ++ eaccess \ + err \ + errx \ + fsync \ +diff --git a/libmount/src/utils.c b/libmount/src/utils.c +index 5c374b432..a275d0a0e 100644 +--- a/libmount/src/utils.c ++++ b/libmount/src/utils.c +@@ -653,18 +653,25 @@ done: + + static int try_write(const char *filename) + { +- int fd; ++ int rc = 0; + + if (!filename) + return -EINVAL; + +- fd = open(filename, O_RDWR|O_CREAT|O_CLOEXEC, ++#ifdef HAVE_EACCESS ++ if (eaccess(filename, R_OK|W_OK) != 0) ++ rc = -errno; ++#else ++ { ++ int fd = open(filename, O_RDWR|O_CREAT|O_CLOEXEC, + S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH); +- if (fd >= 0) { +- close(fd); +- return 0; ++ if (fd < 0) ++ rc = -errno; ++ else ++ close(fd); + } +- return -errno; ++#endif ++ return rc; + } + + /** +-- +2.13.6 + diff --git a/SOURCES/0127-agetty-fix-etc-os-release-parsing.patch b/SOURCES/0127-agetty-fix-etc-os-release-parsing.patch new file mode 100644 index 0000000..0e2e04b --- /dev/null +++ b/SOURCES/0127-agetty-fix-etc-os-release-parsing.patch @@ -0,0 +1,50 @@ +From 6b70da7e86ae17c215c48156a59e76b9f89e8add Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 5 Oct 2017 11:07:41 +0200 +Subject: [PATCH 127/135] agetty: fix /etc/os-release parsing + +For example /etc/os-release: + + VERSION="26 (Twenty Six)" + VERSION_ID=26 + +agetty for \S{VERSION} returns + + _ID=26 + +because the parser does nor check for '=' after variable name. + +Upstream: http://github.com/karelzak/util-linux/commit/949e83997906ab8dd8442f28d74c34bb5a2395ee +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1498462 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + term-utils/agetty.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/term-utils/agetty.c b/term-utils/agetty.c +index 5692126dd..b626cdbeb 100644 +--- a/term-utils/agetty.c ++++ b/term-utils/agetty.c +@@ -1280,6 +1280,7 @@ static char *xgetdomainname(void) + return NULL; + } + ++ + static char *read_os_release(struct options *op, const char *varname) + { + int fd = -1; +@@ -1329,6 +1330,11 @@ static char *read_os_release(struct options *op, const char *varname) + continue; + } + p += varsz; ++ p += strspn(p, " \t\n\r"); ++ ++ if (*p != '=') ++ continue; ++ + p += strspn(p, " \t\n\r=\""); + eol = p + strcspn(p, "\n\r"); + *eol = '\0'; +-- +2.13.6 + diff --git a/SOURCES/0128-fdisk-remove-obsolete-info-about-GPT-from-man-page.patch b/SOURCES/0128-fdisk-remove-obsolete-info-about-GPT-from-man-page.patch new file mode 100644 index 0000000..fb8be18 --- /dev/null +++ b/SOURCES/0128-fdisk-remove-obsolete-info-about-GPT-from-man-page.patch @@ -0,0 +1,33 @@ +From 81ffb923caad0eff603c708cc4d2fe0e3cff295f Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 16 Oct 2017 12:20:40 +0200 +Subject: [PATCH 128/135] fdisk: remove obsolete info about GPT from man page + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1490984 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + fdisks/fdisk.8 | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/fdisks/fdisk.8 b/fdisks/fdisk.8 +index e4e585f12..39e4ad5c3 100644 +--- a/fdisks/fdisk.8 ++++ b/fdisks/fdisk.8 +@@ -33,12 +33,8 @@ fdisk \- manipulate disk partition table + (in the first form of invocation) + is a menu-driven program for creation and manipulation of + partition tables. +-It understands DOS-type partition tables and BSD- or SUN-type disklabels. +- +-.B fdisk +-does not understand GUID partition tables (GPTs) and it is not designed +-for large partitions. In these cases, use the more advanced GNU +-.BR parted (8). ++It understands GPT (experimental for now), MBR, Sun, SGI and BSD ++partition tables. + + .B fdisk + does not use DOS-compatible mode and cylinders as display units by default. +-- +2.13.6 + diff --git a/SOURCES/0129-libfdisk-gpt-sync-type-UUIDs-with-upstream.patch b/SOURCES/0129-libfdisk-gpt-sync-type-UUIDs-with-upstream.patch new file mode 100644 index 0000000..4419581 --- /dev/null +++ b/SOURCES/0129-libfdisk-gpt-sync-type-UUIDs-with-upstream.patch @@ -0,0 +1,112 @@ +From bef54812223745e19561096283653048eae62d90 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 16 Oct 2017 12:28:03 +0200 +Subject: [PATCH 129/135] libfdisk: (gpt) sync type UUIDs with upstream + +This commit updates add types introduced by freedesktop.org, Ceph, +BSD, Plan 9, QNX, Sony, Lenovo, etc. + +Upstream: http://github.com/karelzak/util-linux/commit/5aa8d13b13c3c8eddbd9176ef493dfa70a10616b +Upstream: http://github.com/karelzak/util-linux/commit/5a1b4999f7f9d152654b4af56f73804ce9407844 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1487031 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libfdisk/src/gpt.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 51 insertions(+), 8 deletions(-) + +diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c +index 612ce0971..de30b3dcb 100644 +--- a/libfdisk/src/gpt.c ++++ b/libfdisk/src/gpt.c +@@ -157,27 +157,53 @@ static struct fdisk_parttype gpt_parttypes[] = + DEF_GUID("C12A7328-F81F-11D2-BA4B-00A0C93EC93B", N_("EFI System")), + + DEF_GUID("024DEE41-33E7-11D3-9D69-0008C781F39F", N_("MBR partition scheme")), ++ DEF_GUID("D3BFE2DE-3DAF-11DF-BA40-E3A556D89593", N_("Intel Fast Flash")), ++ + /* Hah!IdontneedEFI */ +- DEF_GUID("21686148-6449-6E6F-744E-656564454649", N_("BIOS boot partition")), ++ DEF_GUID("21686148-6449-6E6F-744E-656564454649", N_("BIOS boot")), ++ ++ /* NIH syndrome */ ++ DEF_GUID("F4019732-066E-4E12-8273-346C5641494F", N_("Sony boot partition")), ++ DEF_GUID("BFBFAFE7-A34F-448A-9A5B-6213EB736C22", N_("Lenovo boot partition")), ++ ++ /* PowerPC reference platform boot partition */ ++ DEF_GUID("9E1A2D38-C612-4316-AA26-8B49521E5A8B", N_("PowerPC PReP boot")), ++ ++ /* Open Network Install Environment */ ++ DEF_GUID("7412F7D5-A156-4B13-81DC-867174929325", N_("ONIE boot")), ++ DEF_GUID("D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149", N_("ONIE config")), + + /* Windows */ + DEF_GUID("E3C9E316-0B5C-4DB8-817D-F92DF00215AE", N_("Microsoft reserved")), + DEF_GUID("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", N_("Microsoft basic data")), + DEF_GUID("5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", N_("Microsoft LDM metadata")), + DEF_GUID("AF9B60A0-1431-4F62-BC68-3311714A69AD", N_("Microsoft LDM data")), +- DEF_GUID("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", N_("Windows recovery evironmnet")), ++ DEF_GUID("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", N_("Windows recovery environment")), + DEF_GUID("37AFFC90-EF7D-4E96-91C3-2D7AE055B174", N_("IBM General Parallel Fs")), ++ DEF_GUID("E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D", N_("Microsoft Storage Spaces")), + + /* HP-UX */ +- DEF_GUID("75894C1E-3AEB-11D3-B7C1-7B03A0000000", N_("HP-UX data partition")), +- DEF_GUID("E2A1E728-32E3-11D6-A682-7B03A0000000", N_("HP-UX service partition")), ++ DEF_GUID("75894C1E-3AEB-11D3-B7C1-7B03A0000000", N_("HP-UX data")), ++ DEF_GUID("E2A1E728-32E3-11D6-A682-7B03A0000000", N_("HP-UX service")), + +- /* Linux */ ++ /* Linux (http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec) */ ++ DEF_GUID("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", N_("Linux swap")), + DEF_GUID("0FC63DAF-8483-4772-8E79-3D69D8477DE4", N_("Linux filesystem")), ++ DEF_GUID("3B8F8425-20E0-4F3B-907F-1A25A76F98E8", N_("Linux server data")), ++ DEF_GUID("44479540-F297-41B2-9AF7-D131D5F0458A", N_("Linux root (x86)")), ++ DEF_GUID("69DAD710-2CE4-4E3C-B16C-21A1D49ABED3", N_("Linux root (ARM)")), ++ DEF_GUID("4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709", N_("Linux root (x86-64)")), ++ DEF_GUID("B921B045-1DF0-41C3-AF44-4C6F280D3FAE", N_("Linux root (ARM-64)")), ++ DEF_GUID("993D8D3D-F80E-4225-855A-9DAF8ED7EA97", N_("Linux root (IA-64)")), ++ DEF_GUID("8DA63339-0007-60C0-C436-083AC8230908", N_("Linux reserved")), ++ DEF_GUID("933AC7E1-2EB4-4F13-B844-0E14E2AEF915", N_("Linux home")), + DEF_GUID("A19D880F-05FC-4D3B-A006-743F0F84911E", N_("Linux RAID")), +- DEF_GUID("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", N_("Linux swap")), ++ DEF_GUID("BC13C2FF-59E6-4262-A352-B275FD6F7172", N_("Linux extended boot")), + DEF_GUID("E6D6D379-F507-44C2-A23C-238F2A3DF928", N_("Linux LVM")), +- DEF_GUID("8DA63339-0007-60C0-C436-083AC8230908", N_("Linux reserved")), ++ /* ... too crazy, ignore for now: ++ DEF_GUID("7FFEC5C9-2D00-49B7-8941-3EA10A5586B7", N_("Linux plain dm-crypt")), ++ DEF_GUID("CA7D7CCB-63ED-4C53-861C-1742536059CC", N_("Linux LUKS")), ++ */ + + /* FreeBSD */ + DEF_GUID("516E7CB4-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD data")), +@@ -230,9 +256,26 @@ static struct fdisk_parttype gpt_parttypes[] = + DEF_GUID("85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD data")), + DEF_GUID("85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD boot")), + DEF_GUID("85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD swap")), +- DEF_GUID("0394Ef8B-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD UFS")), ++ DEF_GUID("0394EF8B-237E-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD UFS")), + DEF_GUID("85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD ZFS")), + DEF_GUID("85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD Vinum")), ++ ++ /* Ceph */ ++ DEF_GUID("45B0969E-9B03-4F30-B4C6-B4B80CEFF106", N_("Ceph Journal")), ++ DEF_GUID("45B0969E-9B03-4F30-B4C6-5EC00CEFF106", N_("Ceph Encrypted Journal")), ++ DEF_GUID("4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D", N_("Ceph OSD")), ++ DEF_GUID("4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D", N_("Ceph crypt OSD")), ++ DEF_GUID("89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE", N_("Ceph disk in creation")), ++ DEF_GUID("89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE", N_("Ceph crypt disk in creation")), ++ ++ /* OpenBSD */ ++ DEF_GUID("824CC7A0-36A8-11E3-890A-952519AD3F61", N_("OpenBSD data")), ++ ++ /* QNX */ ++ DEF_GUID("CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1", N_("QNX6 file system")), ++ ++ /* Plan 9 */ ++ DEF_GUID("C91818F9-8025-47AF-89D2-F030D7000C2C", N_("Plan 9 partition")) + }; + + /* gpt_entry macros */ +-- +2.13.6 + diff --git a/SOURCES/0130-lscpu-cleanup-DMI-detection-return-codes.patch b/SOURCES/0130-lscpu-cleanup-DMI-detection-return-codes.patch new file mode 100644 index 0000000..d88f229 --- /dev/null +++ b/SOURCES/0130-lscpu-cleanup-DMI-detection-return-codes.patch @@ -0,0 +1,87 @@ +From 1b6deafbe0a671d4fd7b8b6e9cc23c8dfcd8683c Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 13 Jun 2017 12:15:11 +0200 +Subject: [PATCH 130/135] lscpu: cleanup DMI detection return codes + +Michal wrote: + There is weird mix of logic in lscpu-dmi.c which sometimes returns 0 and + sometimes -1 on error. Since most checks are if (rc) goto done; this + bails out early on error skipping some detection methods. Further, in + lscpu.c all following detections are guarder by if(hyper) so returning + -1 causes all following methods to be skipped. + +Upstream: http://github.com/karelzak/util-linux/commit/c972852b29391c35b1d5c7d3e1e6413e0cc86908 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1468646 +Reported-by: Michal Suchanek <msuchanek@suse.de> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/lscpu-dmi.c | 21 +++++++++++++-------- + 1 file changed, 13 insertions(+), 8 deletions(-) + +diff --git a/sys-utils/lscpu-dmi.c b/sys-utils/lscpu-dmi.c +index a8298ff74..e4afd0b92 100644 +--- a/sys-utils/lscpu-dmi.c ++++ b/sys-utils/lscpu-dmi.c +@@ -172,7 +172,7 @@ done: + static int hypervisor_decode_legacy(uint8_t *buf, const char *devmem) + { + if (!checksum(buf, 0x0F)) +- return HYPER_NONE; ++ return -1; + + return hypervisor_from_dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), + WORD(buf + 0x0C), +@@ -252,11 +252,15 @@ int read_hypervisor_dmi(void) + || sizeof(uint16_t) != 2 + || sizeof(uint32_t) != 4 + || '\0' != 0) +- return rc; ++ goto done; + ++ /* -1 : no DMI in /sys, ++ * 0 : DMI exist, nothing detected (HYPER_NONE) ++ * >0 : hypervisor detected ++ */ + rc = hypervisor_decode_sysfw(); +- if (rc >= 0) +- return rc; ++ if (rc >= HYPER_NONE) ++ goto done; + + /* First try EFI (ia64, Intel-based Mac) */ + switch (address_from_efi(&fp)) { +@@ -271,8 +275,9 @@ int read_hypervisor_dmi(void) + goto done; + + rc = hypervisor_decode_smbios(buf, _PATH_DEV_MEM); +- if (rc) ++ if (rc >= HYPER_NONE) + goto done; ++ + free(buf); + buf = NULL; + memory_scan: +@@ -285,17 +290,17 @@ memory_scan: + for (fp = 0; fp <= 0xFFF0; fp += 16) { + if (memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) { + rc = hypervisor_decode_smbios(buf + fp, _PATH_DEV_MEM); +- if (rc == -1) ++ if (rc < 0) + fp += 16; + + } else if (memcmp(buf + fp, "_DMI_", 5) == 0) + rc = hypervisor_decode_legacy(buf + fp, _PATH_DEV_MEM); + +- if (rc >= 0) ++ if (rc >= HYPER_NONE) + break; + } + #endif + done: + free(buf); +- return rc; ++ return rc < 0 ? HYPER_NONE : rc; + } +-- +2.13.6 + diff --git a/SOURCES/0131-flock-zero-timeout-is-valid.patch b/SOURCES/0131-flock-zero-timeout-is-valid.patch new file mode 100644 index 0000000..876da3a --- /dev/null +++ b/SOURCES/0131-flock-zero-timeout-is-valid.patch @@ -0,0 +1,50 @@ +From 31568ef6a91b0f441c64fe79f7fa94a32be4dc97 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 7 Oct 2014 12:12:39 +0200 +Subject: [PATCH 131/135] flock: zero timeout is valid + +This patch reverts Sami's "timeout cannot be zero", introduced +in commit 605325b23b36238c8f3ae165e37cab9064553cf7. + +The --timeout 0 has been originally interpreted as --nonblock. The +patch also add hint about this behavior to the man page. + +Upstream: http://github.com/karelzak/util-linux/commit/c4604c38b503c8c46e50fc2048ebbcbcfcad3802 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1455398 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/flock.1 | 4 +++- + sys-utils/flock.c | 2 -- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/sys-utils/flock.1 b/sys-utils/flock.1 +index b28526f69..d5e85e082 100644 +--- a/sys-utils/flock.1 ++++ b/sys-utils/flock.1 +@@ -83,7 +83,9 @@ Fail if the lock cannot be acquired within + Decimal fractional values are allowed. + See the + .I \-E +-option for the exit code used. ++option for the exit code used. The zero number of ++.IR seconds ++is interpreted as \fB\-\-nonblock\fR. + .TP + \fB\-o\fP, \fB\-\-close\fP + Close the file descriptor on which the lock is held before executing +diff --git a/sys-utils/flock.c b/sys-utils/flock.c +index 18625a029..7dad46af0 100644 +--- a/sys-utils/flock.c ++++ b/sys-utils/flock.c +@@ -175,8 +175,6 @@ int main(int argc, char *argv[]) + have_timeout = 1; + strtotimeval_or_err(optarg, &timeout.it_value, + _("invalid timeout value")); +- if (timeout.it_value.tv_sec + timeout.it_value.tv_usec == 0) +- errx(EX_USAGE, _("timeout cannot be zero")); + break; + case 'E': + conflict_exit_code = strtos32_or_err(optarg, +-- +2.13.6 + diff --git a/SOURCES/0132-logger-add-man-page-note-about-the-default-tag.patch b/SOURCES/0132-logger-add-man-page-note-about-the-default-tag.patch new file mode 100644 index 0000000..d1a5411 --- /dev/null +++ b/SOURCES/0132-logger-add-man-page-note-about-the-default-tag.patch @@ -0,0 +1,28 @@ +From b97b85baf6281745b7f720a35d9e4bcc26e9ddcb Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 16 May 2016 15:05:28 +0200 +Subject: [PATCH 132/135] logger: add man page note about the default --tag + +Upstream: http://github.com/karelzak/util-linux/commit/28b6c76f8a4ea1faa80fe779c2011bc433de28c6 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1336432 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + misc-utils/logger.1 | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/misc-utils/logger.1 b/misc-utils/logger.1 +index 57ca0d55a..3b55c8d7d 100644 +--- a/misc-utils/logger.1 ++++ b/misc-utils/logger.1 +@@ -110,6 +110,8 @@ Output the message to standard error as well as to the system log. + \fB\-t\fR, \fB\-\-tag\fR \fItag\fR + Mark every line to be logged with the specified + .IR tag . ++The default tag is the name of the user logged in on the terminal (or a user ++name based on effective user ID). + .TP + \fB\-u\fR, \fB\-\-socket\fR \fIsocket\fR + Write to the specified +-- +2.13.6 + diff --git a/SOURCES/0133-script-use-all-io-to-write.patch b/SOURCES/0133-script-use-all-io-to-write.patch new file mode 100644 index 0000000..090aa9f --- /dev/null +++ b/SOURCES/0133-script-use-all-io-to-write.patch @@ -0,0 +1,61 @@ +From 214d0ef6ac63442c285dd5d9d54190c62faf50d7 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 16 Oct 2017 13:30:44 +0200 +Subject: [PATCH 133/135] script: use all-io to write + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1427500 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + term-utils/script.c | 12 ++++-------- + 1 file changed, 4 insertions(+), 8 deletions(-) + +diff --git a/term-utils/script.c b/term-utils/script.c +index 242b8154a..5840b600d 100644 +--- a/term-utils/script.c ++++ b/term-utils/script.c +@@ -62,6 +62,7 @@ + #include "closestream.h" + #include "nls.h" + #include "c.h" ++#include "all-io.h" + + #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H) + # include <pty.h> +@@ -301,8 +302,7 @@ doinput(void) { + + while (die == 0) { + if ((cc = read(STDIN_FILENO, ibuf, BUFSIZ)) > 0) { +- ssize_t wrt = write(master, ibuf, cc); +- if (wrt < 0) { ++ if (write_all(master, ibuf, cc)) { + warn (_("write failed")); + fail(); + } +@@ -355,8 +355,6 @@ dooutput(FILE *timingfd) { + struct timeval tv; + double oldtime=time(NULL), newtime; + int flgs = 0; +- ssize_t wrt; +- ssize_t fwrt; + + close(STDIN_FILENO); + #ifdef HAVE_LIBUTIL +@@ -393,13 +391,11 @@ dooutput(FILE *timingfd) { + fprintf(timingfd, "%f %zd\n", newtime - oldtime, cc); + oldtime = newtime; + } +- wrt = write(STDOUT_FILENO, obuf, cc); +- if (wrt < 0) { ++ if (write_all(STDOUT_FILENO, obuf, cc)) { + warn (_("write failed")); + fail(); + } +- fwrt = fwrite(obuf, 1, cc, fscript); +- if (fwrt < cc) { ++ if (fwrite_all(obuf, 1, cc, fscript)) { + warn (_("cannot write script file")); + fail(); + } +-- +2.13.6 + diff --git a/SOURCES/0134-logger-do-not-rely-only-getlogin-3-telling-who-ran-t.patch b/SOURCES/0134-logger-do-not-rely-only-getlogin-3-telling-who-ran-t.patch new file mode 100644 index 0000000..c066d9a --- /dev/null +++ b/SOURCES/0134-logger-do-not-rely-only-getlogin-3-telling-who-ran-t.patch @@ -0,0 +1,79 @@ +From 70ed51ccc2e8e6c4976d422af960e4232210fb99 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 16 Oct 2017 13:31:47 +0200 +Subject: [PATCH 134/135] logger: do not rely only getlogin(3) telling who ran + the command + +The getlogin(3) is known not to always work, and when that happens it +is reasonable to try determine user of name by looking process owner +and passwd information. + +Upstream: http://github.com/karelzak/util-linux/commit/019b97024fde3f07eaf541eef990762483369a11 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1336432 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + misc-utils/logger.c | 25 ++++++++++++++++--------- + 1 file changed, 16 insertions(+), 9 deletions(-) + +diff --git a/misc-utils/logger.c b/misc-utils/logger.c +index dfda01866..c1cec45e8 100644 +--- a/misc-utils/logger.c ++++ b/misc-utils/logger.c +@@ -49,6 +49,8 @@ + #include <arpa/inet.h> + #include <netdb.h> + #include <getopt.h> ++#include <sys/types.h> ++#include <pwd.h> + + #include "c.h" + #include "closestream.h" +@@ -183,9 +185,20 @@ inet_socket(const char *servername, const char *port, const int socket_type) + return fd; + } + ++static char const *xgetlogin(void) ++{ ++ char const *cp; ++ struct passwd *pw; ++ ++ if (!(cp = getlogin()) || !*cp) ++ cp = (pw = getpwuid(geteuid()))? pw->pw_name : "<someone>"; ++ return cp; ++} ++ + static void + mysyslog(int fd, int logflags, int pri, char *tag, char *msg) { +- char *buf, pid[30], *cp, *tp; ++ char *buf, pid[30], *tp; ++ const char *cp; + time_t now; + + if (fd > -1) { +@@ -193,13 +206,7 @@ mysyslog(int fd, int logflags, int pri, char *tag, char *msg) { + snprintf (pid, sizeof(pid), "[%d]", getpid()); + else + pid[0] = 0; +- if (tag) +- cp = tag; +- else { +- cp = getlogin(); +- if (!cp) +- cp = "<someone>"; +- } ++ cp = tag ? tag : xgetlogin(); + (void)time(&now); + tp = ctime(&now)+4; + +@@ -334,7 +341,7 @@ main(int argc, char **argv) { + else if (usock) + LogSock = unix_socket(usock, socket_type); + else +- openlog(tag ? tag : getlogin(), logflags, 0); ++ openlog(tag ? tag : xgetlogin(), logflags, 0); + + buf = xcalloc(1, max_message_size); + +-- +2.13.6 + diff --git a/SOURCES/0135-login-use-IPv4-on-IPv4-mapping-to-IPv6.patch b/SOURCES/0135-login-use-IPv4-on-IPv4-mapping-to-IPv6.patch new file mode 100644 index 0000000..8d1c955 --- /dev/null +++ b/SOURCES/0135-login-use-IPv4-on-IPv4-mapping-to-IPv6.patch @@ -0,0 +1,52 @@ +From 7a1a825c7b77785f256e05b1e3ac7676c7485a5c Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 4 Jul 2017 12:50:39 +0200 +Subject: [PATCH 135/135] login: use IPv4 on IPv4-mapping-to-IPv6 + +It seems that on some systems (e.g. RHEL7) the libc function +getaddrinfo() is not able to translate ::ffff: address to IPv4. The +result is 0.0.0.0 host address in the last(1) and utmpdump(1) output. + + /sbin/login -h "::ffff:192.168.1.7" + +utmpdump: + + [7] [03926] [1 ] [user1 ] [pts/1 ] [::ffff:192.168.1.7 ] [0.0.0.0 ] [Thu May 12 17:49:50 2016 ] + +Not sure if this is about order of the getaddrinfo() results, system +configuration or libc version. It's irrelevant for login(1). We have +to be robust enough to write usable address to log files everywhere. + +The solution is to detect IPv4-mapping-to-IPv6 and use IPv4 for utmp. + +Upstream: http://github.com/karelzak/util-linux/commit/1c8792f1ae7fa38cf1d4418ad99c207f65dfdb1a +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1296233 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + login-utils/login.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/login-utils/login.c b/login-utils/login.c +index e0e960f88..5c36953ef 100644 +--- a/login-utils/login.c ++++ b/login-utils/login.c +@@ -1101,8 +1101,15 @@ static void init_remote_info(struct login_context *cxt, char *remotehost) + } else if (info->ai_family == AF_INET6) { + struct sockaddr_in6 *sa = + (struct sockaddr_in6 *) info->ai_addr; ++#ifdef IN6_IS_ADDR_V4MAPPED ++ if (IN6_IS_ADDR_V4MAPPED(&sa->sin6_addr)) { ++ const uint8_t *bytes = sa->sin6_addr.s6_addr; ++ struct in_addr addr = { *(const in_addr_t *) (bytes + 12) }; + +- memcpy(cxt->hostaddress, &(sa->sin6_addr), sizeof(sa->sin6_addr)); ++ memcpy(cxt->hostaddress, &addr, sizeof(struct in_addr)); ++ } else ++#endif ++ memcpy(cxt->hostaddress, &(sa->sin6_addr), sizeof(sa->sin6_addr)); + } + freeaddrinfo(info); + } +-- +2.13.6 + diff --git a/SOURCES/0136-blkid-update-man-page-about-lsblk-and-ambivalent-pro.patch b/SOURCES/0136-blkid-update-man-page-about-lsblk-and-ambivalent-pro.patch new file mode 100644 index 0000000..342f5d9 --- /dev/null +++ b/SOURCES/0136-blkid-update-man-page-about-lsblk-and-ambivalent-pro.patch @@ -0,0 +1,85 @@ +From d7725fb45fe8d4fcb67cb9fbc117bc1cf66fcd56 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 17 Oct 2017 10:44:01 +0200 +Subject: [PATCH 136/141] blkid: update man page about lsblk and ambivalent + probing + +Upstream: http://github.com/karelzak/util-linux/commit/12786a901520d7b30ff75ee8727221bf8a3089cb +Upstream: http://github.com/karelzak/util-linux/commit/27fbfef33c1f23b5448b6cee856bcb3500b33c42 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1501953 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + misc-utils/blkid.8 | 39 +++++++++++++++++++++++++++------------ + 1 file changed, 27 insertions(+), 12 deletions(-) + +diff --git a/misc-utils/blkid.8 b/misc-utils/blkid.8 +index a974b8e99..1f12d202a 100644 +--- a/misc-utils/blkid.8 ++++ b/misc-utils/blkid.8 +@@ -61,6 +61,23 @@ library. It can determine the type of content (e.g. filesystem or swap) + that a block device holds, and also the attributes (tokens, NAME=value pairs) + from the content metadata (e.g. LABEL or UUID fields). + .PP ++.B It is recommended to use ++.BR lsblk (8) ++.B command to get information about block devices, or lsblk --fs to get an overview of filesystems, or ++.BR findmnt (8) ++.B to search in already mounted filesystems. ++.PP ++.RS ++.BR lsblk (8) ++provides more information, better control on output formatting, easy to use in ++scripts and it does not require root permissions to get actual information. ++.B blkid ++reads information directly from devices and for non-root users ++it returns cached unverified information. ++.B blkid ++is mostly designed for system services and to test libblkid functionality. ++.RE ++.PP + When + .I device + is specified, tokens from only this device are displayed. +@@ -71,21 +88,19 @@ If none is given, all devices which appear in + .I /proc/partitions + are shown, if they are recognized. + .PP +-Note that +-.B blkid +-reads information directly from devices and for non-root users +-it returns cached unverified information. It is better to use +-.B lsblk --fs +-to get a user-friendly overview of filesystems and devices. +-.BR lsblk (8) +-is also easy to use in scripts. +-.B blkid +-is mostly designed for system services and to test libblkid functionality. +-.PP + .B blkid + has two main forms of operation: either searching for a device with a + specific NAME=value pair, or displaying NAME=value pairs for one or + more specified devices. ++.PP ++For security reasons ++.B blkid ++silently ignores all devices where the probing result is ambivalent (multiple ++colliding filesystems are detected). The low-level probing mode (\fB-p\fR) ++provides more information and extra return code in this case. ++It's recommended to use ++.BR wipefs (8) ++to get a detailed overview and to erase obsolete stuff (magic strings) from the device. + .SH OPTIONS + The \fIsize\fR and \fIoffset\fR arguments may be followed by the multiplicative + suffixes like KiB (=1024), MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB and YiB +@@ -264,7 +279,7 @@ identified, an exit code of 2 is returned. + + For usage or other errors, an exit code of 4 is returned. + +-If an ambivalent low-level probing result was detected, an exit code of 8 is ++If an ambivalent probing result was detected by low-level probing mode (\fB\-p\fR), an exit code of 8 is + returned. + .SH CONFIGURATION FILE + The standard location of the +-- +2.13.6 + diff --git a/SOURCES/0137-wipefs-fix-t-filter.patch b/SOURCES/0137-wipefs-fix-t-filter.patch new file mode 100644 index 0000000..84e8954 --- /dev/null +++ b/SOURCES/0137-wipefs-fix-t-filter.patch @@ -0,0 +1,69 @@ +From df6302339ae99535d3c075712db8cadeedbb6c00 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 17 Oct 2017 11:07:56 +0200 +Subject: [PATCH 137/141] wipefs: fix -t filter + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1486777 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + misc-utils/wipefs.c | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +diff --git a/misc-utils/wipefs.c b/misc-utils/wipefs.c +index 0144c9ef8..a0852aa97 100644 +--- a/misc-utils/wipefs.c ++++ b/misc-utils/wipefs.c +@@ -162,13 +162,16 @@ clone_offset(struct wipe_desc *wp0) + } + + static struct wipe_desc * +-get_desc_for_probe(struct wipe_desc *wp, blkid_probe pr) ++get_desc_for_probe(struct wipe_desc *wp, blkid_probe pr, int *found) + { + const char *off, *type, *mag, *p, *usage = NULL; + size_t len; + loff_t offset; + int rc, ispt = 0; + ++ if (found) ++ *found = 0; ++ + /* superblocks */ + if (blkid_probe_lookup_value(pr, "TYPE", &type, NULL) == 0) { + rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL); +@@ -215,6 +218,8 @@ get_desc_for_probe(struct wipe_desc *wp, blkid_probe pr) + if (blkid_probe_lookup_value(pr, "UUID", &p, NULL) == 0) + wp->uuid = xstrdup(p); + ++ if (found) ++ *found = 1; + return wp; + } + +@@ -266,7 +271,7 @@ read_offsets(struct wipe_desc *wp, const char *devname) + return NULL; + + while (blkid_do_probe(pr) == 0) { +- wp = get_desc_for_probe(wp, pr); ++ wp = get_desc_for_probe(wp, pr, NULL); + if (!wp) + break; + } +@@ -347,9 +352,13 @@ do_wipe(struct wipe_desc *wp, const char *devname, int noact, int all, int quiet + wp0 = clone_offset(wp); + + while (blkid_do_probe(pr) == 0) { +- wp = get_desc_for_probe(wp, pr); ++ int found = 0; ++ ++ wp = get_desc_for_probe(wp, pr, &found); + if (!wp) + break; ++ if (!found) ++ continue; + + /* Check if offset is in provided list */ + w = wp0; +-- +2.13.6 + diff --git a/SOURCES/0138-tests-backport-new-ts_scsi_debug_init.patch b/SOURCES/0138-tests-backport-new-ts_scsi_debug_init.patch new file mode 100644 index 0000000..e3eba23 --- /dev/null +++ b/SOURCES/0138-tests-backport-new-ts_scsi_debug_init.patch @@ -0,0 +1,105 @@ +From 84995ef8ff7b76cff1cce438fc448f0afa560e23 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 17 Oct 2017 12:16:27 +0200 +Subject: [PATCH 138/141] tests: backport new ts_scsi_debug_init + +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + tests/functions.sh | 76 +++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 66 insertions(+), 10 deletions(-) + +diff --git a/tests/functions.sh b/tests/functions.sh +index 0d1c9c88a..b930dfe7e 100644 +--- a/tests/functions.sh ++++ b/tests/functions.sh +@@ -502,21 +502,77 @@ function ts_fdisk_clean { + } + + function ts_scsi_debug_init { ++ local devname ++ local t ++ TS_DEVICE="none" + +- modprobe --dry-run --quiet scsi_debug +- [ "$?" == 0 ] || ts_skip "missing scsi_debug module" ++ # dry run is not really reliable, real modprobe may still fail ++ modprobe --dry-run --quiet scsi_debug &>/dev/null \ ++ || ts_skip "missing scsi_debug module (dry-run)" + +- rmmod scsi_debug &> /dev/null +- modprobe scsi_debug $* +- [ "$?" == 0 ] || ts_die "Cannot init device" ++ # skip if still in use or removal of modules not supported at all ++ # We don't want a slow timeout here so we don't use ts_scsi_debug_rmmod! ++ modprobe -r scsi_debug &>/dev/null \ ++ || ts_skip "cannot remove scsi_debug module (rmmod)" + +- DEVNAME=$(grep --with-filename scsi_debug /sys/block/*/device/model | awk -F '/' '{print $4}') +- [ "x${DEVNAME}" == "x" ] && ts_die "Cannot find device" ++ modprobe -b scsi_debug "$@" &>/dev/null \ ++ || ts_skip "cannot load scsi_debug module (modprobe)" + +- DEVICE="/dev/${DEVNAME}" ++ # it might be still not loaded, modprobe.conf or whatever ++ lsmod 2>/dev/null | grep -q "^scsi_debug " \ ++ || ts_skip "scsi_debug module not loaded (lsmod)" + +- sleep 1 + udevadm settle + +- echo $DEVICE ++ # wait for device if udevadm settle does not work ++ for t in 0 0.02 0.05 0.1 1; do ++ sleep $t ++ devname=$(grep --with-filename scsi_debug /sys/block/*/device/model) && break ++ done ++ [ -n "${devname}" ] || ts_die "timeout waiting for scsi_debug device" ++ ++ devname=$(echo $devname | awk -F '/' '{print $4}') ++ TS_DEVICE="/dev/${devname}" ++ ++ # TODO validate that device is really up, for now just a warning on stderr ++ test -b $TS_DEVICE || echo "warning: scsi_debug device is still down" >&2 ++} ++ ++# automatically called once in ts_cleanup_on_exit() ++function ts_scsi_debug_rmmod { ++ local err=1 ++ local t ++ local lastmsg ++ ++ # Return early most importantly in case we are not root or the module does ++ # not exist at all. ++ [ $UID -eq 0 ] || return 0 ++ [ -n "$TS_DEVICE" ] || return 0 ++ lsmod 2>/dev/null | grep -q "^scsi_debug " || return 0 ++ ++ udevadm settle ++ ++ # wait for successful rmmod if udevadm settle does not work ++ for t in 0 0.02 0.05 0.1 1; do ++ sleep $t ++ lastmsg="$(modprobe -r scsi_debug 2>&1)" && err=0 && break ++ done ++ ++ if [ "$err" = "1" ]; then ++ ts_log "rmmod failed: '$lastmsg'" ++ ts_log "timeout removing scsi_debug module (rmmod)" ++ return 1 ++ fi ++ if lsmod | grep -q "^scsi_debug "; then ++ ts_log "BUG! scsi_debug still loaded" ++ return 1 ++ fi ++ ++ # TODO Do we need to validate that all devices are gone? ++ udevadm settle ++ test -b "$TS_DEVICE" && echo "warning: scsi_debug device is still up" >&2 ++ ++ # TODO unset TS_DEVICE, check that nobody uses it later, e.g. ts_fdisk_clean ++ ++ return 0 + } +-- +2.13.6 + diff --git a/SOURCES/0139-tests-ts_scsi_debug_init-must-not-run-in-a-subshell.patch b/SOURCES/0139-tests-ts_scsi_debug_init-must-not-run-in-a-subshell.patch new file mode 100644 index 0000000..a2356df --- /dev/null +++ b/SOURCES/0139-tests-ts_scsi_debug_init-must-not-run-in-a-subshell.patch @@ -0,0 +1,558 @@ +From 9e643d190c1c3b39423c4d00f1ddc89e2b4cc09e Mon Sep 17 00:00:00 2001 +From: Ruediger Meier <ruediger.meier@ga-group.nl> +Date: Sun, 11 May 2014 08:50:28 +0200 +Subject: [PATCH 139/141] tests: ts_scsi_debug_init must not run in a subshell + +ts_skip and ts_die won't work from subshell. Now we simply use +TS_DEVICE which is globally set in that function. + +I've made sure that we never change TS_DEVICE variable after +we've got it. So we could use it again for cleanup on exit in +ts_{finalize,die,skip} functions. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1454652 +Upstream: http://github.com/karelzak/util-linux/commit/f45df374ffc311220bd395da985b121eccfb4045 +Signed-off-by: Karel Zak <kzak@redhat.com> +Signed-off-by: Ruediger Meier <ruediger.meier@ga-group.nl> +--- + tests/ts/blkid/md-raid1-part | 25 +++++++++++++++------- + tests/ts/eject/umount | 39 ++++++++++++++++++----------------- + tests/ts/fdisk/align-512-4K | 9 ++++---- + tests/ts/fdisk/align-512-4K-63 | 9 ++++---- + tests/ts/fdisk/align-512-4K-md | 12 ++++++----- + tests/ts/fdisk/align-512-512-topology | 10 ++++----- + tests/ts/libmount/context | 9 ++++---- + tests/ts/libmount/context-utab | 9 ++++---- + tests/ts/libmount/tabfiles-tags | 15 +++++++------- + tests/ts/mount/umount-alltargets | 29 +++++++++++++------------- + tests/ts/mount/umount-recursive | 21 ++++++++++--------- + 11 files changed, 104 insertions(+), 83 deletions(-) + +diff --git a/tests/ts/blkid/md-raid1-part b/tests/ts/blkid/md-raid1-part +index 46bde0ec8..20612ec21 100755 +--- a/tests/ts/blkid/md-raid1-part ++++ b/tests/ts/blkid/md-raid1-part +@@ -23,10 +23,11 @@ TS_DESC="MD raid1 (last partition)" + ts_init "$*" + ts_skip_nonroot + +-DEVICE=$(ts_scsi_debug_init dev_size_mb=51 sector_size=512) ++# set global variable TS_DEVICE ++ts_scsi_debug_init dev_size_mb=51 sector_size=512 + + ts_log "Create partitions" +-$TS_CMD_FDISK ${DEVICE} >> $TS_OUTPUT 2>&1 <<EOF ++$TS_CMD_FDISK ${TS_DEVICE} >> $TS_OUTPUT 2>&1 <<EOF + n + p + 1 +@@ -42,6 +43,16 @@ w + q + EOF + ++# replace generated ID with something stable ++$TS_CMD_FDISK ${TS_DEVICE} &> /dev/null <<EOF ++x ++i ++0x1 ++r ++w ++q ++EOF ++ + udevadm settle + MD_DEVNAME=md8 + MD_DEVICE=/dev/${MD_DEVNAME} +@@ -49,24 +60,24 @@ MD_DEVICE=/dev/${MD_DEVNAME} + ts_log "Create RAID1 device" + /sbin/mdadm -q -S ${MD_DEVICE} &> /dev/null + /sbin/mdadm -q --create ${MD_DEVICE} --metadata=0.90 --chunk=64 --level=1 \ +- --raid-devices=2 ${DEVICE}1 ${DEVICE}2 >> $TS_OUTPUT 2>&1 ++ --raid-devices=2 ${TS_DEVICE}1 ${TS_DEVICE}2 >> $TS_OUTPUT 2>&1 + udevadm settle + + ts_log "Probe whole-disk" +-$TS_CMD_BLKID -p -o udev ${DEVICE} 2>&1 | sort >> $TS_OUTPUT ++$TS_CMD_BLKID -p -o udev ${TS_DEVICE} 2>&1 | sort >> $TS_OUTPUT + + ts_log "Probe first RAID member" +-$TS_CMD_BLKID -p -o udev ${DEVICE}1 2>&1 | sort >> $TS_OUTPUT ++$TS_CMD_BLKID -p -o udev ${TS_DEVICE}1 2>&1 | sort >> $TS_OUTPUT + + ts_log "Probe second RAID member" +-$TS_CMD_BLKID -p -o udev ${DEVICE}2 2>&1 | sort >> $TS_OUTPUT ++$TS_CMD_BLKID -p -o udev ${TS_DEVICE}2 2>&1 | sort >> $TS_OUTPUT + + /sbin/mdadm -q -S ${MD_DEVICE} >> $TS_OUTPUT 2>&1 + + udevadm settle + rmmod scsi_debug + +-ts_fdisk_clean $DEVICE ++ts_fdisk_clean $TS_DEVICE + # substitue UUIDs and major/minor number before comparison + sed -i \ + -e 's/^\(ID_FS_UUID\)=.*/\1=__ts_uuid__/' \ +diff --git a/tests/ts/eject/umount b/tests/ts/eject/umount +index 3c1f84edf..994ddb483 100755 +--- a/tests/ts/eject/umount ++++ b/tests/ts/eject/umount +@@ -12,6 +12,7 @@ ts_skip_nonroot + # not removable device. + # + ++# set global variable TS_DEVICE + function init_device { + ts_scsi_debug_init dev_size_mb=100 + } +@@ -47,50 +48,50 @@ function deinit_device { + + + ts_init_subtest "by-disk" +-DEVICE=$(init_device) +-$TS_CMD_EJECT --force $DEVICE && ts_log "Success" ++init_device ++$TS_CMD_EJECT --force $TS_DEVICE && ts_log "Success" + deinit_device + ts_finalize_subtest + + ts_init_subtest "by-disk-mounted" +-DEVICE=$(init_device) +-mkfs.ext2 -q -F $DEVICE ++init_device ++mkfs.ext2 -q -F $TS_DEVICE + udevadm settle + mkdir -p $TS_MOUNTPOINT +-mount $DEVICE $TS_MOUNTPOINT +-$TS_CMD_EJECT --force $DEVICE && ts_log "Success" ++mount $TS_DEVICE $TS_MOUNTPOINT ++$TS_CMD_EJECT --force $TS_DEVICE && ts_log "Success" + deinit_device + ts_finalize_subtest + + + ts_init_subtest "by-disk-mounted-partition" +-DEVICE=$(init_device) +-init_partitions $DEVICE ++init_device ++init_partitions $TS_DEVICE + mkdir -p ${TS_MOUNTPOINT}1 + mkdir -p ${TS_MOUNTPOINT}2 +-mount ${DEVICE}1 ${TS_MOUNTPOINT}1 +-mount ${DEVICE}2 ${TS_MOUNTPOINT}2 +-$TS_CMD_EJECT --force $DEVICE && ts_log "Success" ++mount ${TS_DEVICE}1 ${TS_MOUNTPOINT}1 ++mount ${TS_DEVICE}2 ${TS_MOUNTPOINT}2 ++$TS_CMD_EJECT --force $TS_DEVICE && ts_log "Success" + deinit_device + ts_finalize_subtest + + + ts_init_subtest "by-partition" +-DEVICE=$(init_device) +-init_partitions $DEVICE +-$TS_CMD_EJECT --force ${DEVICE}1 && ts_log "Success" ++init_device ++init_partitions $TS_DEVICE ++$TS_CMD_EJECT --force ${TS_DEVICE}1 && ts_log "Success" + deinit_device + ts_finalize_subtest + + + ts_init_subtest "by-partition-mounted" +-DEVICE=$(init_device) +-init_partitions $DEVICE ++init_device ++init_partitions $TS_DEVICE + mkdir -p ${TS_MOUNTPOINT}1 + mkdir -p ${TS_MOUNTPOINT}2 +-mount ${DEVICE}1 ${TS_MOUNTPOINT}1 +-mount ${DEVICE}2 ${TS_MOUNTPOINT}2 +-$TS_CMD_EJECT --force ${DEVICE}1 && ts_log "Success" ++mount ${TS_DEVICE}1 ${TS_MOUNTPOINT}1 ++mount ${TS_DEVICE}2 ${TS_MOUNTPOINT}2 ++$TS_CMD_EJECT --force ${TS_DEVICE}1 && ts_log "Success" + deinit_device + ts_finalize_subtest + +diff --git a/tests/ts/fdisk/align-512-4K b/tests/ts/fdisk/align-512-4K +index c5ea72e7a..5608b1b65 100755 +--- a/tests/ts/fdisk/align-512-4K ++++ b/tests/ts/fdisk/align-512-4K +@@ -27,11 +27,12 @@ TS_DESC="align 512/4K" + ts_init "$*" + ts_skip_nonroot + +-DEVICE=$(ts_scsi_debug_init dev_size_mb=50 sector_size=512 physblk_exp=3) +-DEVNAME=$(basename $DEVICE) ++# set global variable TS_DEVICE ++ts_scsi_debug_init dev_size_mb=50 sector_size=512 physblk_exp=3 ++DEVNAME=$(basename $TS_DEVICE) + + ts_log "Create partitions" +-$TS_CMD_FDISK ${DEVICE} >> $TS_OUTPUT 2>&1 <<EOF ++$TS_CMD_FDISK ${TS_DEVICE} >> $TS_OUTPUT 2>&1 <<EOF + n + p + 1 +@@ -71,6 +72,6 @@ cat /sys/block/${DEVNAME}/${DEVNAME}{1,2,3,4,5,6,7}/alignment_offset >> $TS_OUTP + + rmmod scsi_debug + +-ts_fdisk_clean $DEVICE ++ts_fdisk_clean $TS_DEVICE + + ts_finalize +diff --git a/tests/ts/fdisk/align-512-4K-63 b/tests/ts/fdisk/align-512-4K-63 +index 4ec81982f..8199cf7b3 100755 +--- a/tests/ts/fdisk/align-512-4K-63 ++++ b/tests/ts/fdisk/align-512-4K-63 +@@ -27,11 +27,12 @@ TS_DESC="align 512/4K +alignment_offset" + ts_init "$*" + ts_skip_nonroot + +-DEVICE=$(ts_scsi_debug_init dev_size_mb=50 sector_size=512 physblk_exp=3 lowest_aligned=7) +-DEVNAME=$(basename $DEVICE) ++# set global variable TS_DEVICE ++ts_scsi_debug_init dev_size_mb=50 sector_size=512 physblk_exp=3 lowest_aligned=7 ++DEVNAME=$(basename $TS_DEVICE) + + ts_log "Create partitions" +-$TS_CMD_FDISK ${DEVICE} >> $TS_OUTPUT 2>&1 <<EOF ++$TS_CMD_FDISK ${TS_DEVICE} >> $TS_OUTPUT 2>&1 <<EOF + n + p + 1 +@@ -71,6 +72,6 @@ cat /sys/block/${DEVNAME}/${DEVNAME}{1,2,3,4,5,6,7}/alignment_offset >> $TS_OUTP + + rmmod scsi_debug + +-ts_fdisk_clean $DEVICE ++ts_fdisk_clean $TS_DEVICE + + ts_finalize +diff --git a/tests/ts/fdisk/align-512-4K-md b/tests/ts/fdisk/align-512-4K-md +index 09947ff9e..ad662e0e8 100755 +--- a/tests/ts/fdisk/align-512-4K-md ++++ b/tests/ts/fdisk/align-512-4K-md +@@ -27,11 +27,12 @@ TS_DESC="align 512/4K +MD" + ts_init "$*" + ts_skip_nonroot + +-DEVICE=$(ts_scsi_debug_init dev_size_mb=50 sector_size=512 physblk_exp=3) +-DEVNAME=$(basename $DEVICE) ++# set global variable TS_DEVICE ++ts_scsi_debug_init dev_size_mb=50 sector_size=512 physblk_exp=3 ++DEVNAME=$(basename $TS_DEVICE) + + ts_log "Create partitions" +-$TS_CMD_FDISK ${DEVICE} >> $TS_OUTPUT 2>&1 <<EOF ++$TS_CMD_FDISK ${TS_DEVICE} >> $TS_OUTPUT 2>&1 <<EOF + n + p + 1 +@@ -56,7 +57,7 @@ MD_DEVICE=/dev/${MD_DEVNAME} + + /sbin/mdadm -q -S ${MD_DEVICE} &> /dev/null + /sbin/mdadm -q --create ${MD_DEVICE} --metadata=0.90 --chunk=64 \ +- --level=0 --raid-devices=2 ${DEVICE}1 ${DEVICE}2 >> $TS_OUTPUT 2>&1 ++ --level=0 --raid-devices=2 ${TS_DEVICE}1 ${TS_DEVICE}2 >> $TS_OUTPUT 2>&1 + + udevadm settle + ts_log "Create partitions (MD)" +@@ -85,6 +86,7 @@ cat /sys/block/${MD_DEVNAME}/${MD_DEVNAME}p{1,2}/alignment_offset >> $TS_OUTPUT + udevadm settle + rmmod scsi_debug + +-ts_fdisk_clean $DEVICE ++ts_fdisk_clean $TS_DEVICE ++ts_fdisk_clean $MD_DEVICE + + ts_finalize +diff --git a/tests/ts/fdisk/align-512-512-topology b/tests/ts/fdisk/align-512-512-topology +index 9354e45c7..65cb03f1d 100755 +--- a/tests/ts/fdisk/align-512-512-topology ++++ b/tests/ts/fdisk/align-512-512-topology +@@ -27,12 +27,12 @@ TS_DESC="align 512/512 +topology" + ts_init "$*" + ts_skip_nonroot + +- +-DEVICE=$(ts_scsi_debug_init dev_size_mb=50 sector_size=512) +-DEVNAME=$(basename $DEVICE) ++# set global variable TS_DEVICE ++ts_scsi_debug_init dev_size_mb=50 sector_size=512 ++DEVNAME=$(basename $TS_DEVICE) + + ts_log "Create partitions" +-$TS_CMD_FDISK ${DEVICE} >> $TS_OUTPUT 2>&1 <<EOF ++$TS_CMD_FDISK ${TS_DEVICE} >> $TS_OUTPUT 2>&1 <<EOF + n + p + 1 +@@ -72,6 +72,6 @@ cat /sys/block/${DEVNAME}/${DEVNAME}{1,2,3,4,5,6,7}/alignment_offset >> $TS_OUTP + + rmmod scsi_debug + +-ts_fdisk_clean $DEVICE ++ts_fdisk_clean $TS_DEVICE + + ts_finalize +diff --git a/tests/ts/libmount/context b/tests/ts/libmount/context +index 0c3bce986..873890378 100755 +--- a/tests/ts/libmount/context ++++ b/tests/ts/libmount/context +@@ -21,11 +21,12 @@ TS_NOEXIST="$TS_OUTDIR/${TS_TESTNAME}-${TS_SUBNAME}-noex" + ts_log "Init device" + umount $MOUNTPOINT &> /dev/null + +-DEVICE=$(ts_scsi_debug_init dev_size_mb=100) +-DEVNAME=$(basename $DEVICE) ++# set global variable TS_DEVICE ++ts_scsi_debug_init dev_size_mb=100 ++DEVNAME=$(basename $TS_DEVICE) + + ts_log "Create partitions" +-$TS_CMD_FDISK ${DEVICE} &> /dev/null <<EOF ++$TS_CMD_FDISK ${TS_DEVICE} &> /dev/null <<EOF + n + p + 1 +@@ -35,7 +36,7 @@ w + q + EOF + +-DEVICE="${DEVICE}1" ++DEVICE="${TS_DEVICE}1" + + sleep 1 + udevadm settle +diff --git a/tests/ts/libmount/context-utab b/tests/ts/libmount/context-utab +index b2bfefc69..d57a64e6d 100755 +--- a/tests/ts/libmount/context-utab ++++ b/tests/ts/libmount/context-utab +@@ -16,11 +16,12 @@ MOUNTPOINT="$TS_MOUNTPOINT" + + [ -x $TESTPROG ] || ts_skip "test not compiled" + +-DEVICE=$(ts_scsi_debug_init dev_size_mb=100) +-DEVNAME=$(basename $DEVICE) ++# set global variable TS_DEVICE ++ts_scsi_debug_init dev_size_mb=100 ++DEVNAME=$(basename $TS_DEVICE) + + ts_log "Create partitions" +-$TS_CMD_FDISK ${DEVICE} &> /dev/null <<EOF ++$TS_CMD_FDISK ${TS_DEVICE} &> /dev/null <<EOF + n + p + 1 +@@ -30,7 +31,7 @@ w + q + EOF + +-DEVICE="${DEVICE}1" ++DEVICE="${TS_DEVICE}1" + + sleep 1 + udevadm settle +diff --git a/tests/ts/libmount/tabfiles-tags b/tests/ts/libmount/tabfiles-tags +index f52c4047c..fbd7daf47 100755 +--- a/tests/ts/libmount/tabfiles-tags ++++ b/tests/ts/libmount/tabfiles-tags +@@ -11,17 +11,18 @@ TESTPROG="$TS_HELPER_LIBMOUNT_TAB" + + [ -x $TESTPROG ] || ts_skip "test not compiled" + +-DEVICE=$(ts_scsi_debug_init dev_size_mb=50 sector_size=512) ++# set global variable TS_DEVICE ++ts_scsi_debug_init dev_size_mb=50 sector_size=512 + LABEL="testLibmount" + UUID="de1bc6e9-34ab-4151-a1d7-900042eee8d9" + + # + # Create filesystem + # +-mkfs.ext3 -F -L $LABEL $DEVICE -U $UUID &> /dev/null || ts_die "Cannot make ext3 on $DEVICE" $DEVICE ++mkfs.ext3 -F -L $LABEL $TS_DEVICE -U $UUID &> /dev/null || ts_die "Cannot make ext3 on $TS_DEVICE" $TS_DEVICE + udevadm settle + +-ts_device_has_uuid $DEVICE || ts_die "Cannot find UUID on $DEVICE" $DEVICE ++ts_device_has_uuid $TS_DEVICE || ts_die "Cannot find UUID on $TS_DEVICE" $TS_DEVICE + + FSTAB="$TS_OUTDIR/fstab" + +@@ -36,7 +37,7 @@ sed -i -e 's/fs: 0x.*/fs:/g' $TS_OUTPUT + ts_finalize_subtest + + ts_init_subtest "fstab-label2dev" +-ts_valgrind $TESTPROG --find-forward $FSTAB source $DEVICE &> $TS_OUTPUT ++ts_valgrind $TESTPROG --find-forward $FSTAB source $TS_DEVICE &> $TS_OUTPUT + sed -i -e 's/fs: 0x.*/fs:/g' $TS_OUTPUT + ts_finalize_subtest + +@@ -60,18 +61,18 @@ ts_finalize_subtest + + ts_init_subtest "fstab-dev2label" + # has to return /mnt/mountpoint +-ts_valgrind $TESTPROG --find-forward $FSTAB source $DEVICE &> $TS_OUTPUT ++ts_valgrind $TESTPROG --find-forward $FSTAB source $TS_DEVICE &> $TS_OUTPUT + sed -i -e 's/fs: 0x.*/fs:/g' $TS_OUTPUT + ts_finalize_subtest + + # + # Add devname + # +-echo "$DEVICE /mnt/mountpoint3 auto defaults" >> $FSTAB ++echo "$TS_DEVICE /mnt/mountpoint3 auto defaults" >> $FSTAB + + ts_init_subtest "fstab-dev" + # has to return /mnt/mountpoint3 +-ts_valgrind $TESTPROG --find-forward $FSTAB source $DEVICE &> $TS_OUTPUT ++ts_valgrind $TESTPROG --find-forward $FSTAB source $TS_DEVICE &> $TS_OUTPUT + sed -i -e 's/fs: 0x.*/fs:/g' $TS_OUTPUT + sed -i -e 's/source: .*//g' $TS_OUTPUT # devname is generated, remove it + ts_finalize_subtest +diff --git a/tests/ts/mount/umount-alltargets b/tests/ts/mount/umount-alltargets +index 434ecab2d..ae35e320a 100755 +--- a/tests/ts/mount/umount-alltargets ++++ b/tests/ts/mount/umount-alltargets +@@ -12,10 +12,11 @@ ts_skip_nonroot + $TS_CMD_UMOUNT --help | grep -q all-targets + [ $? -eq 1 ] && ts_skip "all-targets unsupported" + +-DEVICE=$(ts_scsi_debug_init dev_size_mb=50) ++# set global variable TS_DEVICE ++ts_scsi_debug_init dev_size_mb=50 + + ts_log "Create partitions" +-$TS_CMD_FDISK ${DEVICE} &> /dev/null <<EOF ++$TS_CMD_FDISK ${TS_DEVICE} &> /dev/null <<EOF + n + p + 1 +@@ -42,13 +43,13 @@ EOF + udevadm settle + + ts_log "Create filesystem A" +-mkfs.ext2 ${DEVICE}1 &> /dev/null ++mkfs.ext2 ${TS_DEVICE}1 &> /dev/null + ts_log "Create filesystem B" +-mkfs.ext2 ${DEVICE}2 &> /dev/null ++mkfs.ext2 ${TS_DEVICE}2 &> /dev/null + ts_log "Create filesystem C" +-mkfs.ext2 ${DEVICE}3 &> /dev/null ++mkfs.ext2 ${TS_DEVICE}3 &> /dev/null + ts_log "Create filesystem D" +-mkfs.ext2 ${DEVICE}4 &> /dev/null ++mkfs.ext2 ${TS_DEVICE}4 &> /dev/null + + udevadm settle + +@@ -76,7 +77,7 @@ MOUNTPOINT=$TS_MOUNTPOINT + # The same device mounted on more places, umount all by mountpoint name + # + ts_init_subtest "all-targets-mnt" +-multi_mount ${DEVICE}1 $MOUNTPOINT ++multi_mount ${TS_DEVICE}1 $MOUNTPOINT + $TS_CMD_UMOUNT --all-targets ${MOUNTPOINT}1 >> $TS_OUTPUT 2>&1 + [ $? == 0 ] || ts_log "umount failed" + ts_finalize_subtest +@@ -85,8 +86,8 @@ ts_finalize_subtest + # The same device mounted on more places, umount all by device name + # + ts_init_subtest "all-targets-dev" +-multi_mount ${DEVICE}1 $MOUNTPOINT +-$TS_CMD_UMOUNT --all-targets ${DEVICE}1 >> $TS_OUTPUT 2>&1 ++multi_mount ${TS_DEVICE}1 $MOUNTPOINT ++$TS_CMD_UMOUNT --all-targets ${TS_DEVICE}1 >> $TS_OUTPUT 2>&1 + [ $? == 0 ] || ts_log "umount failed" + ts_finalize_subtest + +@@ -95,14 +96,14 @@ ts_finalize_subtest + # sub-mounts. Umount all by one umount all by top-level device name. + # + ts_init_subtest "all-targets-recursive" +-multi_mount ${DEVICE}1 $MOUNTPOINT ++multi_mount ${TS_DEVICE}1 $MOUNTPOINT + [ -d "${MOUNTPOINT}1/subA" ] || mkdir -p ${MOUNTPOINT}1/subA +-$TS_CMD_MOUNT ${DEVICE}2 ${MOUNTPOINT}1/subA ++$TS_CMD_MOUNT ${TS_DEVICE}2 ${MOUNTPOINT}1/subA + [ -d "${MOUNTPOINT}1/subA/subAB" ] || mkdir -p ${MOUNTPOINT}1/subA/subAB +-$TS_CMD_MOUNT ${DEVICE}3 ${MOUNTPOINT}1/subA/subAB ++$TS_CMD_MOUNT ${TS_DEVICE}3 ${MOUNTPOINT}1/subA/subAB + [ -d "${MOUNTPOINT}1/subB" ] || mkdir -p ${MOUNTPOINT}1/subB +-$TS_CMD_MOUNT ${DEVICE}4 ${MOUNTPOINT}1/subB +-$TS_CMD_UMOUNT --recursive --all-targets ${DEVICE}1 >> $TS_OUTPUT 2>&1 ++$TS_CMD_MOUNT ${TS_DEVICE}4 ${MOUNTPOINT}1/subB ++$TS_CMD_UMOUNT --recursive --all-targets ${TS_DEVICE}1 >> $TS_OUTPUT 2>&1 + [ $? == 0 ] || ts_log "umount failed" + ts_finalize_subtest + +diff --git a/tests/ts/mount/umount-recursive b/tests/ts/mount/umount-recursive +index 85e54df7a..9b9a7a1f1 100755 +--- a/tests/ts/mount/umount-recursive ++++ b/tests/ts/mount/umount-recursive +@@ -13,10 +13,11 @@ $TS_CMD_UMOUNT --help | grep -q recursive + [ $? -eq 1 ] && ts_skip "recursive unsupported" + + ts_log "Init device" +-DEVICE=$(ts_scsi_debug_init dev_size_mb=50) ++# set global variable TS_DEVICE ++ts_scsi_debug_init dev_size_mb=50 + + ts_log "Create partitions" +-$TS_CMD_FDISK ${DEVICE} &> /dev/null <<EOF ++$TS_CMD_FDISK ${TS_DEVICE} &> /dev/null <<EOF + n + p + 1 +@@ -43,13 +44,13 @@ EOF + udevadm settle + + ts_log "Create filesystem A" +-mkfs.ext2 ${DEVICE}1 &> /dev/null ++mkfs.ext2 ${TS_DEVICE}1 &> /dev/null + ts_log "Create filesystem B" +-mkfs.ext2 ${DEVICE}2 &> /dev/null ++mkfs.ext2 ${TS_DEVICE}2 &> /dev/null + ts_log "Create filesystem C" +-mkfs.ext2 ${DEVICE}3 &> /dev/null ++mkfs.ext2 ${TS_DEVICE}3 &> /dev/null + ts_log "Create filesystem D" +-mkfs.ext2 ${DEVICE}4 &> /dev/null ++mkfs.ext2 ${TS_DEVICE}4 &> /dev/null + + udevadm settle + +@@ -58,12 +59,12 @@ ts_log "Do tests..." + [ -d "$TS_MOUNTPOINT" ] || mkdir -p $TS_MOUNTPOINT + + ts_log "A) Mount root" +-$TS_CMD_MOUNT ${DEVICE}1 $TS_MOUNTPOINT >> $TS_OUTPUT 2>&1 ++$TS_CMD_MOUNT ${TS_DEVICE}1 $TS_MOUNTPOINT >> $TS_OUTPUT 2>&1 + $TS_CMD_MOUNT --make-shared $TS_MOUNTPOINT + + ts_log "B) Mount child" + mkdir -p $TS_MOUNTPOINT/mntB +-$TS_CMD_MOUNT ${DEVICE}2 $TS_MOUNTPOINT/mntB >> $TS_OUTPUT 2>&1 ++$TS_CMD_MOUNT ${TS_DEVICE}2 $TS_MOUNTPOINT/mntB >> $TS_OUTPUT 2>&1 + + ts_log "B2) Mount child-bind" + mkdir -p $TS_MOUNTPOINT/bindB +@@ -71,10 +72,10 @@ $TS_CMD_MOUNT --bind $TS_MOUNTPOINT/mntB $TS_MOUNTPOINT/bindB >> $TS_OUTPUT 2>&1 + + ts_log "C) Mount child/child" + mkdir -p $TS_MOUNTPOINT/mntB/mnt{C,D} +-$TS_CMD_MOUNT ${DEVICE}3 $TS_MOUNTPOINT/mntB/mntC >> $TS_OUTPUT 2>&1 ++$TS_CMD_MOUNT ${TS_DEVICE}3 $TS_MOUNTPOINT/mntB/mntC >> $TS_OUTPUT 2>&1 + + ts_log "D) Mount child/child" +-$TS_CMD_MOUNT ${DEVICE}4 $TS_MOUNTPOINT/mntB/mntD >> $TS_OUTPUT 2>&1 ++$TS_CMD_MOUNT ${TS_DEVICE}4 $TS_MOUNTPOINT/mntB/mntD >> $TS_OUTPUT 2>&1 + + ts_log "E) Mount child-bind" + mkdir -p $TS_MOUNTPOINT/bindC +-- +2.13.6 + diff --git a/SOURCES/0140-tests-cleanup-fdisk-outputs.patch b/SOURCES/0140-tests-cleanup-fdisk-outputs.patch new file mode 100644 index 0000000..37d23a7 --- /dev/null +++ b/SOURCES/0140-tests-cleanup-fdisk-outputs.patch @@ -0,0 +1,86 @@ +From 0c4c724109d160e336d38f0a50fd82c9f5184ddb Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 17 Oct 2017 12:35:31 +0200 +Subject: [PATCH 140/141] tests: cleanup fdisk outputs + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1454652 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + tests/expected/fdisk/align-512-4K-md | 6 +++--- + tests/expected/fdisk/align-512-512 | 16 ++++++++-------- + tests/ts/fdisk/align-512-512 | 3 +-- + 3 files changed, 12 insertions(+), 13 deletions(-) + +diff --git a/tests/expected/fdisk/align-512-4K-md b/tests/expected/fdisk/align-512-4K-md +index e8f6ba161..336c9e019 100644 +--- a/tests/expected/fdisk/align-512-4K-md ++++ b/tests/expected/fdisk/align-512-4K-md +@@ -69,7 +69,7 @@ Select (default p): Partition number (2-4, default 2): First sector (22528-10009 + Last sector, +sectors or +size{K,M,G} (22528-100095, default 100095): Partition 2 of type Linux and of size 10 MiB is set + + Command (m for help): +-Disk /dev/md8: 51 MB, 51249152 bytes, 100096 sectors ++Disk /dev/...: 51 MB, 51249152 bytes, 100096 sectors + Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 4096 bytes + I/O size (minimum/optimal): 65536 bytes / <removed> bytes +@@ -77,8 +77,8 @@ Disk label type: dos + + + Device Boot Start End Blocks Id System +-/dev/md8p1 2048 22527 10240 83 Linux +-/dev/md8p2 22528 43007 10240 83 Linux ++/dev/...p1 2048 22527 10240 83 Linux ++/dev/...p2 22528 43007 10240 83 Linux + + Command (m for help): The partition table has been altered! + +diff --git a/tests/expected/fdisk/align-512-512 b/tests/expected/fdisk/align-512-512 +index 8e03d900d..f9cbb0e89 100644 +--- a/tests/expected/fdisk/align-512-512 ++++ b/tests/expected/fdisk/align-512-512 +@@ -50,7 +50,7 @@ Last sector, +sectors or +size{K,M,G} (90112-102399, default 102399): Using defa + Partition 7 of type Linux and of size 6 MiB is set + + Command (m for help): +-Disk /dev/loop0: 52 MB, 52428800 bytes, 102400 sectors ++Disk /dev/...: 52 MB, 52428800 bytes, 102400 sectors + Units = sectors of 1 * 512 = 512 bytes + Sector size (logical/physical): 512 bytes / 512 bytes + I/O size (minimum/optimal): 512 bytes / <removed> bytes +@@ -58,12 +58,12 @@ Disk label type: dos + + + Device Boot Start End Blocks Id System +-/dev/loop0p1 2048 22527 10240 83 Linux +-/dev/loop0p2 22528 43007 10240 83 Linux +-/dev/loop0p3 43008 63487 10240 83 Linux +-/dev/loop0p4 63488 102399 19456 5 Extended +-/dev/loop0p5 65536 75775 5120 83 Linux +-/dev/loop0p6 77824 88063 5120 83 Linux +-/dev/loop0p7 90112 102399 6144 83 Linux ++/dev/...p1 2048 22527 10240 83 Linux ++/dev/...p2 22528 43007 10240 83 Linux ++/dev/...p3 43008 63487 10240 83 Linux ++/dev/...p4 63488 102399 19456 5 Extended ++/dev/...p5 65536 75775 5120 83 Linux ++/dev/...p6 77824 88063 5120 83 Linux ++/dev/...p7 90112 102399 6144 83 Linux + + Command (m for help): +diff --git a/tests/ts/fdisk/align-512-512 b/tests/ts/fdisk/align-512-512 +index 82a6268cf..03133a6d1 100755 +--- a/tests/ts/fdisk/align-512-512 ++++ b/tests/ts/fdisk/align-512-512 +@@ -64,7 +64,6 @@ q + EOF + + ts_device_deinit $DEVICE +- +-ts_fdisk_clean ++ts_fdisk_clean $DEVICE + + ts_finalize +-- +2.13.6 + diff --git a/SOURCES/0141-tests-check-for-mdadm.patch b/SOURCES/0141-tests-check-for-mdadm.patch new file mode 100644 index 0000000..c2fc6c7 --- /dev/null +++ b/SOURCES/0141-tests-check-for-mdadm.patch @@ -0,0 +1,53 @@ +From 609177ec200592dce34bf0ee082c0d2ac8d01b8b Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 17 Oct 2017 12:35:31 +0200 +Subject: [PATCH 141/141] tests: check for mdadm + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1454652 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + tests/ts/blkid/md-raid1-part | 1 + + tests/ts/eject/umount | 2 +- + tests/ts/fdisk/align-512-4K-md | 1 + + 3 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/tests/ts/blkid/md-raid1-part b/tests/ts/blkid/md-raid1-part +index 20612ec21..de21bca9a 100755 +--- a/tests/ts/blkid/md-raid1-part ++++ b/tests/ts/blkid/md-raid1-part +@@ -22,6 +22,7 @@ TS_DESC="MD raid1 (last partition)" + . $TS_TOPDIR/functions.sh + ts_init "$*" + ts_skip_nonroot ++ts_check_prog "mdadm" + + # set global variable TS_DEVICE + ts_scsi_debug_init dev_size_mb=51 sector_size=512 +diff --git a/tests/ts/eject/umount b/tests/ts/eject/umount +index 994ddb483..e5cfe2b14 100755 +--- a/tests/ts/eject/umount ++++ b/tests/ts/eject/umount +@@ -43,7 +43,7 @@ EOF + + function deinit_device { + udevadm settle +- rmmod scsi_debug ++ ts_scsi_debug_rmmod + } + + +diff --git a/tests/ts/fdisk/align-512-4K-md b/tests/ts/fdisk/align-512-4K-md +index ad662e0e8..c45a0d43b 100755 +--- a/tests/ts/fdisk/align-512-4K-md ++++ b/tests/ts/fdisk/align-512-4K-md +@@ -26,6 +26,7 @@ TS_DESC="align 512/4K +MD" + . $TS_TOPDIR/functions.sh + ts_init "$*" + ts_skip_nonroot ++ts_check_prog "mdadm" + + # set global variable TS_DEVICE + ts_scsi_debug_init dev_size_mb=50 sector_size=512 physblk_exp=3 +-- +2.13.6 + diff --git a/SOURCES/0142-lsmem-chmem-backport-new-commands.patch b/SOURCES/0142-lsmem-chmem-backport-new-commands.patch new file mode 100644 index 0000000..c20edd1 --- /dev/null +++ b/SOURCES/0142-lsmem-chmem-backport-new-commands.patch @@ -0,0 +1,1608 @@ +From 4fde7a853f61fa025a9d42a7571161a69fdae162 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 23 Oct 2017 15:54:54 +0200 +Subject: [PATCH] lsmem, chmem: backport new commands + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1496421 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + configure.ac | 19 ++ + include/c.h | 10 + + include/path.h | 5 +- + include/strutils.h | 1 + + lib/path.c | 36 ++- + lib/strutils.c | 17 +- + sys-utils/Makemodule.am | 14 + + sys-utils/chmem.8 | 114 ++++++++ + sys-utils/chmem.c | 447 +++++++++++++++++++++++++++++++ + sys-utils/lsmem.1 | 95 +++++++ + sys-utils/lsmem.c | 687 ++++++++++++++++++++++++++++++++++++++++++++++++ + 11 files changed, 1432 insertions(+), 13 deletions(-) + create mode 100644 sys-utils/chmem.8 + create mode 100644 sys-utils/chmem.c + create mode 100644 sys-utils/lsmem.1 + create mode 100644 sys-utils/lsmem.c + +diff --git a/configure.ac b/configure.ac +index 96c5838cf..d561e01d0 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1138,6 +1138,25 @@ UL_REQUIRES_SYSCALL_CHECK([pivot_root], [UL_CHECK_SYSCALL([pivot_root])]) + AM_CONDITIONAL(BUILD_PIVOT_ROOT, test "x$build_pivot_root" = xyes) + + ++AC_ARG_ENABLE([lsmem], ++ AS_HELP_STRING([--disable-lsmem], [do not build lsmem]), ++ [], enable_lsmem=check ++) ++UL_BUILD_INIT([lsmem]) ++UL_REQUIRES_LINUX([lsmem]) ++UL_REQUIRES_BUILD([lsmem], [libsmartcols]) ++AM_CONDITIONAL([BUILD_LSMEM], [test "x$build_lsmem" = xyes]) ++ ++ ++AC_ARG_ENABLE([chmem], ++ AS_HELP_STRING([--disable-chmem], [do not build chmem]), ++ [], enable_chmem=check ++) ++UL_BUILD_INIT([chmem]) ++UL_REQUIRES_LINUX([chmem]) ++AM_CONDITIONAL([BUILD_CHMEM], [test "x$build_chmem" = xyes]) ++ ++ + AC_ARG_ENABLE([elvtune], + AS_HELP_STRING([--enable-elvtune], [build elvtune (only works with 2.2 and 2.4 kernels)]), + [], enable_elvtune=no +diff --git a/include/c.h b/include/c.h +index 8ff61b484..124035ea5 100644 +--- a/include/c.h ++++ b/include/c.h +@@ -302,6 +302,16 @@ static inline int usleep(useconds_t usec) + + #define UTIL_LINUX_VERSION _("%s from %s\n"), program_invocation_short_name, PACKAGE_STRING + ++/* backported to RHEL */ ++#define USAGE_COLUMNS _("\nAvailable output columns:\n") ++#define USAGE_OPTSTR_HELP _("display this help") ++#define USAGE_OPTSTR_VERSION _("display version") ++#define USAGE_HELP_OPTIONS(marg_dsc) \ ++ "%-" #marg_dsc "s%s\n" \ ++ "%-" #marg_dsc "s%s\n" \ ++ , " -h, --help", USAGE_OPTSTR_HELP \ ++ , " -V, --version", USAGE_OPTSTR_VERSION ++ + /* + * scanf modifiers for "strings allocation" + */ +diff --git a/include/path.h b/include/path.h +index 45da692f8..1ab5724fc 100644 +--- a/include/path.h ++++ b/include/path.h +@@ -4,6 +4,10 @@ + #include <stdio.h> + #include <stdint.h> + ++extern int path_set_prefix(const char *); ++ ++extern const char *path_get(const char *path, ...); ++ + extern char *path_strdup(const char *path, ...) + __attribute__ ((__format__ (__printf__, 1, 2))); + extern FILE *path_fopen(const char *mode, int exit_on_err, const char *path, ...) +@@ -27,7 +31,6 @@ extern cpu_set_t *path_read_cpuset(int, const char *path, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); + extern cpu_set_t *path_read_cpulist(int, const char *path, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); +-extern void path_set_prefix(const char *); + #endif /* HAVE_CPU_SET_T */ + + #endif /* UTIL_LINUX_PATH_H */ +diff --git a/include/strutils.h b/include/strutils.h +index aa7b95f96..1f028e4ed 100644 +--- a/include/strutils.h ++++ b/include/strutils.h +@@ -25,6 +25,7 @@ extern uint32_t strtou32_or_err(const char *str, const char *errmesg); + + extern int64_t strtos64_or_err(const char *str, const char *errmesg); + extern uint64_t strtou64_or_err(const char *str, const char *errmesg); ++extern uint64_t strtox64_or_err(const char *str, const char *errmesg); + + extern double strtod_or_err(const char *str, const char *errmesg); + +diff --git a/lib/path.c b/lib/path.c +index e47d31418..49ca9b5e0 100644 +--- a/lib/path.c ++++ b/lib/path.c +@@ -38,6 +38,20 @@ + static size_t prefixlen; + static char pathbuf[PATH_MAX]; + ++int ++path_set_prefix(const char *prefix) ++{ ++ size_t len = strlen(prefix); ++ ++ if (len >= sizeof(pathbuf) - 1) { ++ errno = ENAMETOOLONG; ++ return -1; ++ } ++ prefixlen = len; ++ strcpy(pathbuf, prefix); ++ return 0; ++} ++ + static const char * + path_vcreate(const char *path, va_list ap) + { +@@ -49,6 +63,19 @@ path_vcreate(const char *path, va_list ap) + return pathbuf; + } + ++const char * ++path_get(const char *path, ...) ++{ ++ const char *p; ++ va_list ap; ++ ++ va_start(ap, path); ++ p = path_vcreate(path, ap); ++ va_end(ap); ++ ++ return p; ++} ++ + char * + path_strdup(const char *path, ...) + { +@@ -246,13 +273,6 @@ path_read_cpulist(int maxcpus, const char *path, ...) + + return set; + } +- + #endif /* HAVE_CPU_SET_T */ + +-void +-path_set_prefix(const char *prefix) +-{ +- prefixlen = strlen(prefix); +- strncpy(pathbuf, prefix, sizeof(pathbuf)); +- pathbuf[sizeof(pathbuf) - 1] = '\0'; +-} ++ +diff --git a/lib/strutils.c b/lib/strutils.c +index 4b8a8130d..2458a2c2f 100644 +--- a/lib/strutils.c ++++ b/lib/strutils.c +@@ -237,27 +237,36 @@ err: + errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); + } + +-uint64_t strtou64_or_err(const char *str, const char *errmesg) ++static uint64_t _strtou64_or_err(const char *str, const char *errmesg, int base) + { + uintmax_t num; + char *end = NULL; + ++ errno = 0; + if (str == NULL || *str == '\0') + goto err; +- errno = 0; +- num = strtoumax(str, &end, 10); ++ num = strtoumax(str, &end, base); + + if (errno || str == end || (end && *end)) + goto err; + + return num; + err: +- if (errno) ++ if (errno == ERANGE) + err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); + + errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); + } + ++uint64_t strtou64_or_err(const char *str, const char *errmesg) ++{ ++ return _strtou64_or_err(str, errmesg, 10); ++} ++ ++uint64_t strtox64_or_err(const char *str, const char *errmesg) ++{ ++ return _strtou64_or_err(str, errmesg, 16); ++} + + double strtod_or_err(const char *str, const char *errmesg) + { +diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am +index 0496b84e3..21585ce80 100644 +--- a/sys-utils/Makemodule.am ++++ b/sys-utils/Makemodule.am +@@ -1,3 +1,17 @@ ++if BUILD_LSMEM ++usrbin_exec_PROGRAMS += lsmem ++dist_man_MANS += sys-utils/lsmem.1 ++lsmem_SOURCES = sys-utils/lsmem.c ++lsmem_LDADD = $(LDADD) libcommon.la libsmartcols.la ++lsmem_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) ++endif ++ ++if BUILD_CHMEM ++usrbin_exec_PROGRAMS += chmem ++dist_man_MANS += sys-utils/chmem.8 ++chmem_SOURCES = sys-utils/chmem.c ++chmem_LDADD = $(LDADD) libcommon.la ++endif + + usrbin_exec_PROGRAMS += flock + dist_man_MANS += sys-utils/flock.1 +diff --git a/sys-utils/chmem.8 b/sys-utils/chmem.8 +new file mode 100644 +index 000000000..dae7413d4 +--- /dev/null ++++ b/sys-utils/chmem.8 +@@ -0,0 +1,114 @@ ++.TH CHMEM 8 "October 2016" "util-linux" "System Administration" ++.SH NAME ++chmem \- configure memory ++.SH SYNOPSIS ++.B chmem ++.RB [ \-h "] [" \-V "] [" \-v "] [" \-e | \-d "]" ++[\fISIZE\fP|\fIRANGE\fP|\fB\-b\fP \fIBLOCKRANGE\fP] ++[-z ZONE] ++.SH DESCRIPTION ++The chmem command sets a particular size or range of memory online or offline. ++. ++.IP "\(hy" 2 ++Specify \fISIZE\fP as <size>[m|M|g|G]. With m or M, <size> specifies the memory ++size in MiB (1024 x 1024 bytes). With g or G, <size> specifies the memory size ++in GiB (1024 x 1024 x 1024 bytes). The default unit is MiB. ++. ++.IP "\(hy" 2 ++Specify \fIRANGE\fP in the form 0x<start>-0x<end> as shown in the output of the ++\fBlsmem\fP command. <start> is the hexadecimal address of the first byte and <end> ++is the hexadecimal address of the last byte in the memory range. ++. ++.IP "\(hy" 2 ++Specify \fIBLOCKRANGE\fP in the form <first>-<last> or <block> as shown in the ++output of the \fBlsmem\fP command. <first> is the number of the first memory block ++and <last> is the number of the last memory block in the memory ++range. Alternatively a single block can be specified. \fIBLOCKRANGE\fP requires ++the \fB--blocks\fP option. ++. ++.IP "\(hy" 2 ++Specify \fIZONE\fP as the name of a memory zone, as shown in the output of the ++\fBlsmem -o +ZONES\fP command. The output shows one or more valid memory zones ++for each memory range. If multiple zones are shown, then the memory range ++currently belongs to the first zone. By default, chmem will set memory online ++to the zone Movable, if this is among the valid zones. This default can be ++changed by specifying the \fB--zone\fP option with another valid zone. ++For memory ballooning, it is recommended to select the zone Movable for memory ++online and offline, if possible. Memory in this zone is much more likely to be ++able to be offlined again, but it cannot be used for arbitrary kernel ++allocations, only for migratable pages (e.g. anonymous and page cache pages). ++Use the \fB\-\-help\fR option to see all available zones. ++. ++.PP ++\fISIZE\fP and \fIRANGE\fP must be aligned to the Linux memory block size, as ++shown in the output of the \fBlsmem\fP command. ++ ++Setting memory online can fail for various reasons. On virtualized systems it ++can fail if the hypervisor does not have enough memory left, for example ++because memory was overcommitted. Setting memory offline can fail if Linux ++cannot free the memory. If only part of the requested memory can be set online ++or offline, a message tells you how much memory was set online or offline ++instead of the requested amount. ++ ++When setting memory online \fBchmem\fP starts with the lowest memory block ++numbers. When setting memory offline \fBchmem\fP starts with the highest memory ++block numbers. ++.SH OPTIONS ++.TP ++.BR \-b ", " \-\-blocks ++Use a \fIBLOCKRANGE\fP parameter instead of \fIRANGE\fP or \fISIZE\fP for the ++\fB--enable\fP and \fB--disable\fP options. ++.TP ++.BR \-d ", " \-\-disable ++Set the specified \fIRANGE\fP, \fISIZE\fP, or \fIBLOCKRANGE\fP of memory offline. ++.TP ++.BR \-e ", " \-\-enable ++Set the specified \fIRANGE\fP, \fISIZE\fP, or \fIBLOCKRANGE\fP of memory online. ++.TP ++.BR \-z ", " \-\-zone ++Select the memory \fIZONE\fP where to set the specified \fIRANGE\fP, \fISIZE\fP, ++or \fIBLOCKRANGE\fP of memory online or offline. By default, memory will be set ++online to the zone Movable, if possible. ++.TP ++.BR \-h ", " \-\-help ++Print a short help text, then exit. ++.TP ++.BR \-v ", " \-\-verbose ++Verbose mode. Causes \fBchmem\fP to print debugging messages about it's ++progress. ++.TP ++.BR \-V ", " \-\-version ++Print the version number, then exit. ++.SH RETURN CODES ++.B chmem ++has the following return codes: ++.TP ++.BR 0 ++success ++.TP ++.BR 1 ++failure ++.TP ++.BR 64 ++partial success ++.SH EXAMPLES ++.TP ++.B chmem --enable 1024 ++This command requests 1024 MiB of memory to be set online. ++.TP ++.B chmem -e 2g ++This command requests 2 GiB of memory to be set online. ++.TP ++.B chmem --disable 0x00000000e4000000-0x00000000f3ffffff ++This command requests the memory range starting with 0x00000000e4000000 ++and ending with 0x00000000f3ffffff to be set offline. ++.TP ++.B chmem -b -d 10 ++This command requests the memory block number 10 to be set offline. ++.SH SEE ALSO ++.BR lsmem (1) ++.SH AVAILABILITY ++The \fBchmem\fP command is part of the util-linux package and is available from ++.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/ ++Linux Kernel Archive ++.UE . +diff --git a/sys-utils/chmem.c b/sys-utils/chmem.c +new file mode 100644 +index 000000000..0e5e84727 +--- /dev/null ++++ b/sys-utils/chmem.c +@@ -0,0 +1,447 @@ ++/* ++ * chmem - Memory configuration tool ++ * ++ * Copyright IBM Corp. 2016 ++ * ++ * 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 <stdio.h> ++#include <unistd.h> ++#include <stdlib.h> ++#include <getopt.h> ++#include <assert.h> ++#include <dirent.h> ++#include <ctype.h> ++ ++#include "c.h" ++#include "nls.h" ++#include "path.h" ++#include "strutils.h" ++#include "strv.h" ++#include "optutils.h" ++#include "closestream.h" ++#include "xalloc.h" ++ ++/* partial success, otherwise we return regular EXIT_{SUCCESS,FAILURE} */ ++#define CHMEM_EXIT_SOMEOK 64 ++ ++#define _PATH_SYS_MEMORY "/sys/devices/system/memory" ++#define _PATH_SYS_MEMORY_BLOCK_SIZE _PATH_SYS_MEMORY "/block_size_bytes" ++ ++struct chmem_desc { ++ struct dirent **dirs; ++ int ndirs; ++ uint64_t block_size; ++ uint64_t start; ++ uint64_t end; ++ uint64_t size; ++ unsigned int use_blocks : 1; ++ unsigned int is_size : 1; ++ unsigned int verbose : 1; ++ unsigned int have_zones : 1; ++}; ++ ++enum { ++ CMD_MEMORY_ENABLE = 0, ++ CMD_MEMORY_DISABLE, ++ CMD_NONE ++}; ++ ++enum zone_id { ++ ZONE_DMA = 0, ++ ZONE_DMA32, ++ ZONE_NORMAL, ++ ZONE_HIGHMEM, ++ ZONE_MOVABLE, ++ ZONE_DEVICE, ++}; ++ ++static char *zone_names[] = { ++ [ZONE_DMA] = "DMA", ++ [ZONE_DMA32] = "DMA32", ++ [ZONE_NORMAL] = "Normal", ++ [ZONE_HIGHMEM] = "Highmem", ++ [ZONE_MOVABLE] = "Movable", ++ [ZONE_DEVICE] = "Device", ++}; ++ ++/* ++ * name must be null-terminated ++ */ ++static int zone_name_to_id(const char *name) ++{ ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(zone_names); i++) { ++ if (!strcasecmp(name, zone_names[i])) ++ return i; ++ } ++ return -1; ++} ++ ++static void idxtostr(struct chmem_desc *desc, uint64_t idx, char *buf, size_t bufsz) ++{ ++ uint64_t start, end; ++ ++ start = idx * desc->block_size; ++ end = start + desc->block_size - 1; ++ snprintf(buf, bufsz, ++ _("Memory Block %"PRIu64" (0x%016"PRIx64"-0x%016"PRIx64")"), ++ idx, start, end); ++} ++ ++static int chmem_size(struct chmem_desc *desc, int enable, int zone_id) ++{ ++ char *name, *onoff, line[BUFSIZ], str[BUFSIZ]; ++ uint64_t size, index; ++ const char *zn; ++ int i, rc; ++ ++ size = desc->size; ++ onoff = enable ? "online" : "offline"; ++ i = enable ? 0 : desc->ndirs - 1; ++ ++ if (enable && zone_id >= 0) { ++ if (zone_id == ZONE_MOVABLE) ++ onoff = "online_movable"; ++ else ++ onoff = "online_kernel"; ++ } ++ ++ for (; i >= 0 && i < desc->ndirs && size; i += enable ? 1 : -1) { ++ name = desc->dirs[i]->d_name; ++ index = strtou64_or_err(name + 6, _("Failed to parse index")); ++ path_read_str(line, sizeof(line), _PATH_SYS_MEMORY "/%s/state", name); ++ if (strncmp(onoff, line, 6) == 0) ++ continue; ++ ++ if (desc->have_zones) { ++ path_read_str(line, sizeof(line), ++ _PATH_SYS_MEMORY "/%s/valid_zones", name); ++ if (zone_id >= 0) { ++ zn = zone_names[zone_id]; ++ if (enable && !strcasestr(line, zn)) ++ continue; ++ if (!enable && strncasecmp(line, zn, strlen(zn))) ++ continue; ++ } else if (enable) { ++ /* By default, use zone Movable for online, if valid */ ++ if (strcasestr(line, zone_names[ZONE_MOVABLE])) ++ onoff = "online_movable"; ++ else ++ onoff = "online"; ++ } ++ } ++ ++ idxtostr(desc, index, str, sizeof(str)); ++ rc = path_write_str(onoff, _PATH_SYS_MEMORY"/%s/state", name); ++ if (rc == -1 && desc->verbose) { ++ if (enable) ++ fprintf(stdout, _("%s enable failed\n"), str); ++ else ++ fprintf(stdout, _("%s disable failed\n"), str); ++ } else if (rc == 0 && desc->verbose) { ++ if (enable) ++ fprintf(stdout, _("%s enabled\n"), str); ++ else ++ fprintf(stdout, _("%s disabled\n"), str); ++ } ++ if (rc == 0) ++ size--; ++ } ++ if (size) { ++ uint64_t bytes; ++ char *sizestr; ++ ++ bytes = (desc->size - size) * desc->block_size; ++ sizestr = size_to_human_string(SIZE_SUFFIX_1LETTER, bytes); ++ if (enable) ++ warnx(_("Could only enable %s of memory"), sizestr); ++ else ++ warnx(_("Could only disable %s of memory"), sizestr); ++ free(sizestr); ++ } ++ return size == 0 ? 0 : size == desc->size ? -1 : 1; ++} ++ ++static int chmem_range(struct chmem_desc *desc, int enable, int zone_id) ++{ ++ char *name, *onoff, line[BUFSIZ], str[BUFSIZ]; ++ uint64_t index, todo; ++ const char *zn; ++ int i, rc; ++ ++ todo = desc->end - desc->start + 1; ++ onoff = enable ? "online" : "offline"; ++ ++ if (enable && zone_id >= 0) { ++ if (zone_id == ZONE_MOVABLE) ++ onoff = "online_movable"; ++ else ++ onoff = "online_kernel"; ++ } ++ ++ for (i = 0; i < desc->ndirs; i++) { ++ name = desc->dirs[i]->d_name; ++ index = strtou64_or_err(name + 6, _("Failed to parse index")); ++ if (index < desc->start) ++ continue; ++ if (index > desc->end) ++ break; ++ idxtostr(desc, index, str, sizeof(str)); ++ path_read_str(line, sizeof(line), _PATH_SYS_MEMORY "/%s/state", name); ++ if (strncmp(onoff, line, 6) == 0) { ++ if (desc->verbose && enable) ++ fprintf(stdout, _("%s already enabled\n"), str); ++ else if (desc->verbose && !enable) ++ fprintf(stdout, _("%s already disabled\n"), str); ++ todo--; ++ continue; ++ } ++ ++ if (desc->have_zones) { ++ path_read_str(line, sizeof(line), ++ _PATH_SYS_MEMORY "/%s/valid_zones", name); ++ if (zone_id >= 0) { ++ zn = zone_names[zone_id]; ++ if (enable && !strcasestr(line, zn)) { ++ warnx(_("%s enable failed: Zone mismatch"), str); ++ continue; ++ } ++ if (!enable && strncasecmp(line, zn, strlen(zn))) { ++ warnx(_("%s disable failed: Zone mismatch"), str); ++ continue; ++ } ++ } else if (enable) { ++ /* By default, use zone Movable for online, if valid */ ++ if (strcasestr(line, zone_names[ZONE_MOVABLE])) ++ onoff = "online_movable"; ++ else ++ onoff = "online"; ++ } ++ } ++ ++ rc = path_write_str(onoff, _PATH_SYS_MEMORY"/%s/state", name); ++ if (rc == -1) { ++ if (enable) ++ warn(_("%s enable failed"), str); ++ else ++ warn(_("%s disable failed"), str); ++ } else if (desc->verbose) { ++ if (enable) ++ fprintf(stdout, _("%s enabled\n"), str); ++ else ++ fprintf(stdout, _("%s disabled\n"), str); ++ } ++ if (rc == 0) ++ todo--; ++ } ++ return todo == 0 ? 0 : todo == desc->end - desc->start + 1 ? -1 : 1; ++} ++ ++static int filter(const struct dirent *de) ++{ ++ if (strncmp("memory", de->d_name, 6)) ++ return 0; ++ return isdigit_string(de->d_name + 6); ++} ++ ++static void read_info(struct chmem_desc *desc) ++{ ++ char line[BUFSIZ]; ++ ++ desc->ndirs = scandir(_PATH_SYS_MEMORY, &desc->dirs, filter, versionsort); ++ if (desc->ndirs <= 0) ++ err(EXIT_FAILURE, _("Failed to read %s"), _PATH_SYS_MEMORY); ++ path_read_str(line, sizeof(line), _PATH_SYS_MEMORY_BLOCK_SIZE); ++ desc->block_size = strtoumax(line, NULL, 16); ++} ++ ++static void parse_single_param(struct chmem_desc *desc, char *str) ++{ ++ if (desc->use_blocks) { ++ desc->start = strtou64_or_err(str, _("Failed to parse block number")); ++ desc->end = desc->start; ++ return; ++ } ++ desc->is_size = 1; ++ desc->size = strtosize_or_err(str, _("Failed to parse size")); ++ if (isdigit(str[strlen(str) - 1])) ++ desc->size *= 1024*1024; ++ if (desc->size % desc->block_size) { ++ errx(EXIT_FAILURE, _("Size must be aligned to memory block size (%s)"), ++ size_to_human_string(SIZE_SUFFIX_1LETTER, desc->block_size)); ++ } ++ desc->size /= desc->block_size; ++} ++ ++static void parse_range_param(struct chmem_desc *desc, char *start, char *end) ++{ ++ if (desc->use_blocks) { ++ desc->start = strtou64_or_err(start, _("Failed to parse start")); ++ desc->end = strtou64_or_err(end, _("Failed to parse end")); ++ return; ++ } ++ if (strlen(start) < 2 || start[1] != 'x') ++ errx(EXIT_FAILURE, _("Invalid start address format: %s"), start); ++ if (strlen(end) < 2 || end[1] != 'x') ++ errx(EXIT_FAILURE, _("Invalid end address format: %s"), end); ++ desc->start = strtox64_or_err(start, _("Failed to parse start address")); ++ desc->end = strtox64_or_err(end, _("Failed to parse end address")); ++ if (desc->start % desc->block_size || (desc->end + 1) % desc->block_size) { ++ errx(EXIT_FAILURE, ++ _("Start address and (end address + 1) must be aligned to " ++ "memory block size (%s)"), ++ size_to_human_string(SIZE_SUFFIX_1LETTER, desc->block_size)); ++ } ++ desc->start /= desc->block_size; ++ desc->end /= desc->block_size; ++} ++ ++static void parse_parameter(struct chmem_desc *desc, char *param) ++{ ++ char **split; ++ ++ split = strv_split(param, "-"); ++ if (strv_length(split) > 2) ++ errx(EXIT_FAILURE, _("Invalid parameter: %s"), param); ++ if (strv_length(split) == 1) ++ parse_single_param(desc, split[0]); ++ else ++ parse_range_param(desc, split[0], split[1]); ++ strv_free(split); ++ if (desc->start > desc->end) ++ errx(EXIT_FAILURE, _("Invalid range: %s"), param); ++} ++ ++static void __attribute__((__noreturn__)) usage(void) ++{ ++ FILE *out = stdout; ++ size_t i; ++ ++ fputs(USAGE_HEADER, out); ++ fprintf(out, _(" %s [options] [SIZE|RANGE|BLOCKRANGE]\n"), program_invocation_short_name); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Set a particular size or range of memory online or offline.\n"), out); ++ ++ fputs(USAGE_OPTIONS, out); ++ fputs(_(" -e, --enable enable memory\n"), out); ++ fputs(_(" -d, --disable disable memory\n"), out); ++ fputs(_(" -b, --blocks use memory blocks\n"), out); ++ fputs(_(" -z, --zone <name> select memory zone (see below)\n"), out); ++ fputs(_(" -v, --verbose verbose output\n"), out); ++ printf(USAGE_HELP_OPTIONS(20)); ++ ++ fputs(_("\nSupported zones:\n"), out); ++ for (i = 0; i < ARRAY_SIZE(zone_names); i++) ++ fprintf(out, " %s\n", zone_names[i]); ++ ++ printf(USAGE_MAN_TAIL("chmem(8)")); ++ ++ exit(EXIT_SUCCESS); ++} ++ ++int main(int argc, char **argv) ++{ ++ struct chmem_desc _desc = { }, *desc = &_desc; ++ int cmd = CMD_NONE, zone_id = -1; ++ char *zone = NULL; ++ int c, rc; ++ ++ static const struct option longopts[] = { ++ {"block", no_argument, NULL, 'b'}, ++ {"disable", no_argument, NULL, 'd'}, ++ {"enable", no_argument, NULL, 'e'}, ++ {"help", no_argument, NULL, 'h'}, ++ {"verbose", no_argument, NULL, 'v'}, ++ {"version", no_argument, NULL, 'V'}, ++ {"zone", required_argument, NULL, 'z'}, ++ {NULL, 0, NULL, 0} ++ }; ++ ++ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ ++ { 'd','e' }, ++ { 0 } ++ }; ++ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; ++ ++ setlocale(LC_ALL, ""); ++ bindtextdomain(PACKAGE, LOCALEDIR); ++ textdomain(PACKAGE); ++ atexit(close_stdout); ++ ++ read_info(desc); ++ ++ while ((c = getopt_long(argc, argv, "bdehvVz:", longopts, NULL)) != -1) { ++ ++ err_exclusive_options(c, longopts, excl, excl_st); ++ ++ switch (c) { ++ case 'd': ++ cmd = CMD_MEMORY_DISABLE; ++ break; ++ case 'e': ++ cmd = CMD_MEMORY_ENABLE; ++ break; ++ case 'b': ++ desc->use_blocks = 1; ++ break; ++ case 'h': ++ usage(); ++ break; ++ case 'v': ++ desc->verbose = 1; ++ break; ++ case 'V': ++ printf(UTIL_LINUX_VERSION); ++ return EXIT_SUCCESS; ++ case 'z': ++ zone = xstrdup(optarg); ++ break; ++ default: ++ errtryhelp(EXIT_FAILURE); ++ } ++ } ++ ++ if ((argc == 1) || (argc != optind + 1) || (cmd == CMD_NONE)) { ++ warnx(_("bad usage")); ++ errtryhelp(EXIT_FAILURE); ++ } ++ ++ parse_parameter(desc, argv[optind]); ++ ++ /* The valid_zones sysfs attribute was introduced with kernel 3.18 */ ++ if (path_exist(_PATH_SYS_MEMORY "/memory0/valid_zones")) ++ desc->have_zones = 1; ++ else if (zone) ++ warnx(_("zone ignored, no valid_zones sysfs attribute present")); ++ ++ if (zone && desc->have_zones) { ++ zone_id = zone_name_to_id(zone); ++ if (zone_id == -1) { ++ warnx(_("unknown memory zone: %s"), zone); ++ errtryhelp(EXIT_FAILURE); ++ } ++ } ++ ++ if (desc->is_size) ++ rc = chmem_size(desc, cmd == CMD_MEMORY_ENABLE ? 1 : 0, zone_id); ++ else ++ rc = chmem_range(desc, cmd == CMD_MEMORY_ENABLE ? 1 : 0, zone_id); ++ ++ return rc == 0 ? EXIT_SUCCESS : ++ rc < 0 ? EXIT_FAILURE : CHMEM_EXIT_SOMEOK; ++} +diff --git a/sys-utils/lsmem.1 b/sys-utils/lsmem.1 +new file mode 100644 +index 000000000..3f5cd7d4b +--- /dev/null ++++ b/sys-utils/lsmem.1 +@@ -0,0 +1,95 @@ ++.TH LSMEM 1 "October 2016" "util-linux" "User Commands" ++.SH NAME ++lsmem \- list the ranges of available memory with their online status ++.SH SYNOPSIS ++.B lsmem ++[options] ++.SH DESCRIPTION ++The \fBlsmem\fP command lists the ranges of available memory with their online ++status. The listed memory blocks correspond to the memory block representation ++in sysfs. The command also shows the memory block size and the amount of memory ++in online and offline state. ++ ++The default output compatible with original implementation from s390-tools, but ++it's strongly recommended to avoid using default outputs in your scripts. ++Always explicitly define expected columns by using the \fB\-\-output\fR option ++together with a columns list in environments where a stable output is required. ++ ++The \fBlsmem\fP command lists a new memory range always when the current memory ++block distinguish from the previous block by STATE, REMOVABLE, NODE or ZONES ++attribute. This default behavior is possible to override by the ++\fB\-\-split\fR option (e.g. \fBlsmem \-\-split=STATE,ZONES\fR). The special ++word "none" may be used to ignore all differences between memory blocks and to ++create as large as possible continuous ranges. The opposite semantic is ++\fB\-\-all\fR to list individual memory blocks. The default split policy is ++subject to change. Always explicitly use \fB\-\-split\fR in environments where ++a stable output is required. ++ ++Note that some output columns may provide inaccurate information if a split policy ++forces \fBlsmem\fP to ignore diffrences in some attributes. For example if you ++merge removable and non-removable memory blocks to the one range than all ++the range will be marked as non-removable on \fBlsmem\fP output. ++ ++Not all columns are supported on all systems. If an unsupported column is ++specified, \fBlsmem\fP prints the column but does not provide any data for it. ++ ++Use the \fB\-\-help\fR option to see the columns description. ++ ++.SH OPTIONS ++.TP ++.BR \-a ", " \-\-all ++List each individual memory block, instead of combining memory blocks with ++similar attributes. ++.TP ++.BR \-b , " \-\-bytes" ++Print the SIZE column in bytes rather than in a human-readable format. ++.TP ++.BR \-h ", " \-\-help ++Display help text and exit. ++.TP ++.BR \-n , " \-\-noheadings" ++Do not print a header line. ++.TP ++.BR \-o , " \-\-output " \fIlist\fP ++Specify which output columns to print. Use \fB\-\-help\fR ++to get a list of all supported columns. ++The default list of columns may be extended if \fIlist\fP is ++specified in the format \fB+\fIlist\fP (e.g. \fBlsmem \-o +NODE\fP). ++.TP ++.BR \-P , " \-\-pairs" ++Produce output in the form of key="value" pairs. ++All potentially unsafe characters are hex-escaped (\\x<code>). ++.TP ++.BR \-r , " \-\-raw" ++Produce output in raw format. All potentially unsafe characters are hex-escaped ++(\\x<code>). ++.TP ++.BR \-S , " \-\-split " \fIlist\fP ++Specify which columns (attributes) use to split memory blocks to ranges. The ++supported columns are STATE, REMOVABLE, NODE and ZONES, or "none". The another columns are ++silently ignored. For more details see DESCRIPTION above. ++.TP ++.BR \-s , " \-\-sysroot " \fIdirectory\fP ++Gather memory data for a Linux instance other than the instance from which the ++\fBlsmem\fP command is issued. The specified \fIdirectory\fP is the system ++root of the Linux instance to be inspected. ++.TP ++.BR \-V ", " \-\-version ++Display version information and exit. ++.TP ++\fB\-\-summary\fR[=\fIwhen\fR] ++This option controls summary lines output. The optional argument \fIwhen\fP can be ++\fBnever\fR, \fBalways\fR or \fBonly\fR. If the \fIwhen\fR argument is ++omitted, it defaults to \fB"only"\fR. The summary output is suppresed for ++\fB\-\-raw\fR and \fB\-\-pairs\fR. ++.SH AUTHOR ++.B lsmem ++was originally written by Gerald Schaefer for s390-tools in Perl. The C version ++for util-linux was written by Clemens von Mann, Heiko Carstens and Karel Zak. ++.SH SEE ALSO ++.BR chmem (8) ++.SH AVAILABILITY ++The \fBlsmem\fP command is part of the util-linux package and is available from ++.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/ ++Linux Kernel Archive ++.UE . +diff --git a/sys-utils/lsmem.c b/sys-utils/lsmem.c +new file mode 100644 +index 000000000..34a2847af +--- /dev/null ++++ b/sys-utils/lsmem.c +@@ -0,0 +1,687 @@ ++/* ++ * lsmem - Show memory configuration ++ * ++ * Copyright IBM Corp. 2016 ++ * Copyright (C) 2016 Karel Zak <kzak@redhat.com> ++ * ++ * 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 <c.h> ++#include <nls.h> ++#include <path.h> ++#include <strutils.h> ++#include <closestream.h> ++#include <xalloc.h> ++#include <getopt.h> ++#include <stdio.h> ++#include <stdlib.h> ++#include <dirent.h> ++#include <fcntl.h> ++#include <inttypes.h> ++#include <assert.h> ++#include <optutils.h> ++#include <libsmartcols.h> ++ ++#define _PATH_SYS_MEMORY "/sys/devices/system/memory" ++#define _PATH_SYS_MEMORY_BLOCK_SIZE _PATH_SYS_MEMORY "/block_size_bytes" ++ ++#define MEMORY_STATE_ONLINE 0 ++#define MEMORY_STATE_OFFLINE 1 ++#define MEMORY_STATE_GOING_OFFLINE 2 ++#define MEMORY_STATE_UNKNOWN 3 ++ ++enum zone_id { ++ ZONE_DMA = 0, ++ ZONE_DMA32, ++ ZONE_NORMAL, ++ ZONE_HIGHMEM, ++ ZONE_MOVABLE, ++ ZONE_DEVICE, ++ ZONE_NONE, ++ ZONE_UNKNOWN, ++ MAX_NR_ZONES, ++}; ++ ++struct memory_block { ++ uint64_t index; ++ uint64_t count; ++ int state; ++ int node; ++ int nr_zones; ++ int zones[MAX_NR_ZONES]; ++ unsigned int removable:1; ++}; ++ ++struct lsmem { ++ struct dirent **dirs; ++ int ndirs; ++ struct memory_block *blocks; ++ int nblocks; ++ uint64_t block_size; ++ uint64_t mem_online; ++ uint64_t mem_offline; ++ ++ struct libscols_table *table; ++ unsigned int have_nodes : 1, ++ raw : 1, ++ export : 1, ++ noheadings : 1, ++ summary : 1, ++ list_all : 1, ++ bytes : 1, ++ want_summary : 1, ++ want_table : 1, ++ split_by_node : 1, ++ split_by_state : 1, ++ split_by_removable : 1, ++ split_by_zones : 1, ++ have_zones : 1; ++}; ++ ++ ++enum { ++ COL_RANGE, ++ COL_SIZE, ++ COL_STATE, ++ COL_REMOVABLE, ++ COL_BLOCK, ++ COL_NODE, ++ COL_ZONES, ++}; ++ ++static char *zone_names[] = { ++ [ZONE_DMA] = "DMA", ++ [ZONE_DMA32] = "DMA32", ++ [ZONE_NORMAL] = "Normal", ++ [ZONE_HIGHMEM] = "Highmem", ++ [ZONE_MOVABLE] = "Movable", ++ [ZONE_DEVICE] = "Device", ++ [ZONE_NONE] = "None", /* block contains more than one zone, can't be offlined */ ++ [ZONE_UNKNOWN] = "Unknown", ++}; ++ ++/* column names */ ++struct coldesc { ++ const char *name; /* header */ ++ double whint; /* width hint (N < 1 is in percent of termwidth) */ ++ int flags; /* SCOLS_FL_* */ ++ const char *help; ++ ++ int sort_type; /* SORT_* */ ++}; ++ ++/* columns descriptions */ ++static struct coldesc coldescs[] = { ++ [COL_RANGE] = { "RANGE", 0, 0, N_("start and end address of the memory range")}, ++ [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("size of the memory range")}, ++ [COL_STATE] = { "STATE", 0, SCOLS_FL_RIGHT, N_("online status of the memory range")}, ++ [COL_REMOVABLE] = { "REMOVABLE", 0, SCOLS_FL_RIGHT, N_("memory is removable")}, ++ [COL_BLOCK] = { "BLOCK", 0, SCOLS_FL_RIGHT, N_("memory block number or blocks range")}, ++ [COL_NODE] = { "NODE", 0, SCOLS_FL_RIGHT, N_("numa node of memory")}, ++ [COL_ZONES] = { "ZONES", 0, SCOLS_FL_RIGHT, N_("valid zones for the memory range")}, ++}; ++ ++/* 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 size_t 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; ++} ++ ++/* ++ * name must be null-terminated ++ */ ++static int zone_name_to_id(const char *name) ++{ ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(zone_names); i++) { ++ if (!strcasecmp(name, zone_names[i])) ++ return i; ++ } ++ return ZONE_UNKNOWN; ++} ++ ++#define add_column(ary, n, id) \ ++ ((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id)) ++ ++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 inline int get_column_id(int num) ++{ ++ assert(num >= 0); ++ assert((size_t) num < ncolumns); ++ assert(columns[num] < (int) ARRAY_SIZE(coldescs)); ++ ++ return columns[num]; ++} ++ ++static inline struct coldesc *get_column_desc(int num) ++{ ++ return &coldescs[ get_column_id(num) ]; ++} ++ ++static inline void reset_split_policy(struct lsmem *l, int enable) ++{ ++ l->split_by_state = enable; ++ l->split_by_node = enable; ++ l->split_by_removable = enable; ++ l->split_by_zones = enable; ++} ++ ++static void add_scols_line(struct lsmem *lsmem, struct memory_block *blk) ++{ ++ size_t i; ++ struct libscols_line *line; ++ ++ line = scols_table_new_line(lsmem->table, NULL); ++ if (!line) ++ err_oom(); ++ ++ for (i = 0; i < ncolumns; i++) { ++ char *str = NULL; ++ ++ switch (get_column_id(i)) { ++ case COL_RANGE: ++ { ++ uint64_t start = blk->index * lsmem->block_size; ++ uint64_t size = blk->count * lsmem->block_size; ++ xasprintf(&str, "0x%016"PRIx64"-0x%016"PRIx64, start, start + size - 1); ++ break; ++ } ++ case COL_SIZE: ++ if (lsmem->bytes) ++ xasprintf(&str, "%"PRId64, (uint64_t) blk->count * lsmem->block_size); ++ else ++ str = size_to_human_string(SIZE_SUFFIX_1LETTER, ++ (uint64_t) blk->count * lsmem->block_size); ++ break; ++ case COL_STATE: ++ str = xstrdup( ++ blk->state == MEMORY_STATE_ONLINE ? _("online") : ++ blk->state == MEMORY_STATE_OFFLINE ? _("offline") : ++ blk->state == MEMORY_STATE_GOING_OFFLINE ? _("on->off") : ++ "?"); ++ break; ++ case COL_REMOVABLE: ++ if (blk->state == MEMORY_STATE_ONLINE) ++ str = xstrdup(blk->removable ? _("yes") : _("no")); ++ else ++ str = xstrdup("-"); ++ break; ++ case COL_BLOCK: ++ if (blk->count == 1) ++ xasprintf(&str, "%"PRId64, blk->index); ++ else ++ xasprintf(&str, "%"PRId64"-%"PRId64, ++ blk->index, blk->index + blk->count - 1); ++ break; ++ case COL_NODE: ++ if (lsmem->have_nodes) ++ xasprintf(&str, "%d", blk->node); ++ else ++ str = xstrdup("-"); ++ break; ++ case COL_ZONES: ++ if (lsmem->have_zones) { ++ char valid_zones[BUFSIZ]; ++ int j, zone_id; ++ ++ valid_zones[0] = '\0'; ++ for (j = 0; j < blk->nr_zones; j++) { ++ zone_id = blk->zones[j]; ++ if (strlen(valid_zones) + ++ strlen(zone_names[zone_id]) > BUFSIZ - 2) ++ break; ++ strcat(valid_zones, zone_names[zone_id]); ++ if (j + 1 < blk->nr_zones) ++ strcat(valid_zones, "/"); ++ } ++ str = xstrdup(valid_zones); ++ } else ++ str = xstrdup("-"); ++ break; ++ } ++ ++ if (str && scols_line_refer_data(line, i, str) != 0) ++ err_oom(); ++ } ++} ++ ++static void fill_scols_table(struct lsmem *lsmem) ++{ ++ int i; ++ ++ for (i = 0; i < lsmem->nblocks; i++) ++ add_scols_line(lsmem, &lsmem->blocks[i]); ++} ++ ++static void print_summary(struct lsmem *lsmem) ++{ ++ if (lsmem->bytes) { ++ printf("%-23s %15"PRId64"\n",_("Memory block size:"), lsmem->block_size); ++ printf("%-23s %15"PRId64"\n",_("Total online memory:"), lsmem->mem_online); ++ printf("%-23s %15"PRId64"\n",_("Total offline memory:"), lsmem->mem_offline); ++ } else { ++ printf("%-23s %5s\n",_("Memory block size:"), ++ size_to_human_string(SIZE_SUFFIX_1LETTER, lsmem->block_size)); ++ printf("%-23s %5s\n",_("Total online memory:"), ++ size_to_human_string(SIZE_SUFFIX_1LETTER, lsmem->mem_online)); ++ printf("%-23s %5s\n",_("Total offline memory:"), ++ size_to_human_string(SIZE_SUFFIX_1LETTER, lsmem->mem_offline)); ++ } ++} ++ ++static int memory_block_get_node(char *name) ++{ ++ struct dirent *de; ++ const char *path; ++ DIR *dir; ++ int node; ++ ++ path = path_get(_PATH_SYS_MEMORY"/%s", name); ++ if (!path || !(dir= opendir(path))) ++ err(EXIT_FAILURE, _("Failed to open %s"), path ? path : name); ++ ++ node = -1; ++ while ((de = readdir(dir)) != NULL) { ++ if (strncmp("node", de->d_name, 4)) ++ continue; ++ if (!isdigit_string(de->d_name + 4)) ++ continue; ++ node = strtol(de->d_name + 4, NULL, 10); ++ break; ++ } ++ closedir(dir); ++ return node; ++} ++ ++static void memory_block_read_attrs(struct lsmem *lsmem, char *name, ++ struct memory_block *blk) ++{ ++ char *token = NULL; ++ char line[BUFSIZ]; ++ int i; ++ ++ blk->count = 1; ++ blk->index = strtoumax(name + 6, NULL, 10); /* get <num> of "memory<num>" */ ++ blk->removable = path_read_u64(_PATH_SYS_MEMORY"/%s/removable", name); ++ blk->state = MEMORY_STATE_UNKNOWN; ++ ++ path_read_str(line, sizeof(line), _PATH_SYS_MEMORY"/%s/state", name); ++ if (strcmp(line, "offline") == 0) ++ blk->state = MEMORY_STATE_OFFLINE; ++ else if (strcmp(line, "online") == 0) ++ blk->state = MEMORY_STATE_ONLINE; ++ else if (strcmp(line, "going-offline") == 0) ++ blk->state = MEMORY_STATE_GOING_OFFLINE; ++ ++ if (lsmem->have_nodes) ++ blk->node = memory_block_get_node(name); ++ ++ blk->nr_zones = 0; ++ if (lsmem->have_zones) { ++ path_read_str(line, sizeof(line), _PATH_SYS_MEMORY"/%s/valid_zones", name); ++ token = strtok(line, " "); ++ } ++ for (i = 0; i < MAX_NR_ZONES; i++) { ++ if (token) { ++ blk->zones[i] = zone_name_to_id(token); ++ blk->nr_zones++; ++ token = strtok(NULL, " "); ++ } ++ } ++} ++ ++static int is_mergeable(struct lsmem *lsmem, struct memory_block *blk) ++{ ++ struct memory_block *curr; ++ int i; ++ ++ if (!lsmem->nblocks) ++ return 0; ++ curr = &lsmem->blocks[lsmem->nblocks - 1]; ++ if (lsmem->list_all) ++ return 0; ++ if (curr->index + curr->count != blk->index) ++ return 0; ++ if (lsmem->split_by_state && curr->state != blk->state) ++ return 0; ++ if (lsmem->split_by_removable && curr->removable != blk->removable) ++ return 0; ++ if (lsmem->split_by_node && lsmem->have_nodes) { ++ if (curr->node != blk->node) ++ return 0; ++ } ++ if (lsmem->split_by_zones && lsmem->have_zones) { ++ if (curr->nr_zones != blk->nr_zones) ++ return 0; ++ for (i = 0; i < curr->nr_zones; i++) { ++ if (curr->zones[i] == ZONE_UNKNOWN || ++ curr->zones[i] != blk->zones[i]) ++ return 0; ++ } ++ } ++ return 1; ++} ++ ++static void read_info(struct lsmem *lsmem) ++{ ++ struct memory_block blk; ++ char line[BUFSIZ]; ++ int i; ++ ++ path_read_str(line, sizeof(line), _PATH_SYS_MEMORY_BLOCK_SIZE); ++ lsmem->block_size = strtoumax(line, NULL, 16); ++ ++ for (i = 0; i < lsmem->ndirs; i++) { ++ memory_block_read_attrs(lsmem, lsmem->dirs[i]->d_name, &blk); ++ if (is_mergeable(lsmem, &blk)) { ++ lsmem->blocks[lsmem->nblocks - 1].count++; ++ continue; ++ } ++ lsmem->nblocks++; ++ lsmem->blocks = xrealloc(lsmem->blocks, lsmem->nblocks * sizeof(blk)); ++ *&lsmem->blocks[lsmem->nblocks - 1] = blk; ++ } ++ for (i = 0; i < lsmem->nblocks; i++) { ++ if (lsmem->blocks[i].state == MEMORY_STATE_ONLINE) ++ lsmem->mem_online += lsmem->block_size * lsmem->blocks[i].count; ++ else ++ lsmem->mem_offline += lsmem->block_size * lsmem->blocks[i].count; ++ } ++} ++ ++static int memory_block_filter(const struct dirent *de) ++{ ++ if (strncmp("memory", de->d_name, 6)) ++ return 0; ++ return isdigit_string(de->d_name + 6); ++} ++ ++static void read_basic_info(struct lsmem *lsmem) ++{ ++ const char *dir; ++ ++ if (!path_exist(_PATH_SYS_MEMORY_BLOCK_SIZE)) ++ errx(EXIT_FAILURE, _("This system does not support memory blocks")); ++ ++ dir = path_get(_PATH_SYS_MEMORY); ++ if (!dir) ++ err(EXIT_FAILURE, _("Failed to read %s"), _PATH_SYS_MEMORY); ++ ++ lsmem->ndirs = scandir(dir, &lsmem->dirs, memory_block_filter, versionsort); ++ if (lsmem->ndirs <= 0) ++ err(EXIT_FAILURE, _("Failed to read %s"), _PATH_SYS_MEMORY); ++ ++ if (memory_block_get_node(lsmem->dirs[0]->d_name) != -1) ++ lsmem->have_nodes = 1; ++ ++ /* The valid_zones sysfs attribute was introduced with kernel 3.18 */ ++ if (path_exist(_PATH_SYS_MEMORY "/memory0/valid_zones")) ++ lsmem->have_zones = 1; ++} ++ ++static void __attribute__((__noreturn__)) usage(void) ++{ ++ FILE *out = stdout; ++ size_t i; ++ ++ fputs(USAGE_HEADER, out); ++ fprintf(out, _(" %s [options]\n"), program_invocation_short_name); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("List the ranges of available memory with their online status.\n"), out); ++ ++ fputs(USAGE_OPTIONS, out); ++ fputs(_(" -P, --pairs use key=\"value\" output format\n"), out); ++ fputs(_(" -a, --all list each individual memory block\n"), out); ++ fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out); ++ fputs(_(" -n, --noheadings don't print headings\n"), out); ++ fputs(_(" -o, --output <list> output columns\n"), out); ++ fputs(_(" -r, --raw use raw output format\n"), out); ++ fputs(_(" -S, --split <list> split ranges by specified columns\n"), out); ++ fputs(_(" -s, --sysroot <dir> use the specified directory as system root\n"), out); ++ fputs(_(" --summary[=when] print summary information (never,always or only)\n"), out); ++ ++ fputs(USAGE_SEPARATOR, out); ++ printf(USAGE_HELP_OPTIONS(22)); ++ ++ fputs(USAGE_COLUMNS, out); ++ for (i = 0; i < ARRAY_SIZE(coldescs); i++) ++ fprintf(out, " %10s %s\n", coldescs[i].name, _(coldescs[i].help)); ++ ++ printf(USAGE_MAN_TAIL("lsmem(1)")); ++ ++ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); ++} ++ ++int main(int argc, char **argv) ++{ ++ struct lsmem _lsmem = { ++ .want_table = 1, ++ .want_summary = 1 ++ }, *lsmem = &_lsmem; ++ ++ const char *outarg = NULL, *splitarg = NULL; ++ int c; ++ size_t i; ++ ++ enum { ++ LSMEM_OPT_SUMARRY = CHAR_MAX + 1 ++ }; ++ ++ static const struct option longopts[] = { ++ {"all", no_argument, NULL, 'a'}, ++ {"bytes", no_argument, NULL, 'b'}, ++ {"help", no_argument, NULL, 'h'}, ++ {"noheadings", no_argument, NULL, 'n'}, ++ {"output", required_argument, NULL, 'o'}, ++ {"pairs", no_argument, NULL, 'P'}, ++ {"raw", no_argument, NULL, 'r'}, ++ {"sysroot", required_argument, NULL, 's'}, ++ {"split", required_argument, NULL, 'S'}, ++ {"version", no_argument, NULL, 'V'}, ++ {"summary", optional_argument, NULL, LSMEM_OPT_SUMARRY }, ++ {NULL, 0, NULL, 0} ++ }; ++ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ ++ { 'J', 'P', 'r' }, ++ { 'S', 'a' }, ++ { 0 } ++ }; ++ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; ++ ++ setlocale(LC_ALL, ""); ++ bindtextdomain(PACKAGE, LOCALEDIR); ++ textdomain(PACKAGE); ++ atexit(close_stdout); ++ ++ while ((c = getopt_long(argc, argv, "abhJno:PrS:s:V", longopts, NULL)) != -1) { ++ ++ err_exclusive_options(c, longopts, excl, excl_st); ++ ++ switch (c) { ++ case 'a': ++ lsmem->list_all = 1; ++ break; ++ case 'b': ++ lsmem->bytes = 1; ++ break; ++ case 'h': ++ usage(); ++ break; ++ case 'n': ++ lsmem->noheadings = 1; ++ break; ++ case 'o': ++ outarg = optarg; ++ break; ++ case 'P': ++ lsmem->export = 1; ++ lsmem->want_summary = 0; ++ break; ++ case 'r': ++ lsmem->raw = 1; ++ lsmem->want_summary = 0; ++ break; ++ case 's': ++ if(path_set_prefix(optarg)) ++ err(EXIT_FAILURE, _("invalid argument to %s"), "--sysroot"); ++ break; ++ case 'S': ++ splitarg = optarg; ++ break; ++ case 'V': ++ printf(UTIL_LINUX_VERSION); ++ return 0; ++ case LSMEM_OPT_SUMARRY: ++ if (optarg) { ++ if (strcmp(optarg, "never") == 0) ++ lsmem->want_summary = 0; ++ else if (strcmp(optarg, "only") == 0) ++ lsmem->want_table = 0; ++ else if (strcmp(optarg, "always") == 0) ++ lsmem->want_summary = 1; ++ else ++ errx(EXIT_FAILURE, _("unsupported --summary argument")); ++ } else ++ lsmem->want_table = 0; ++ break; ++ default: ++ errtryhelp(EXIT_FAILURE); ++ } ++ } ++ ++ if (argc != optind) { ++ warnx(_("bad usage")); ++ errtryhelp(EXIT_FAILURE); ++ } ++ ++ if (lsmem->want_table + lsmem->want_summary == 0) ++ errx(EXIT_FAILURE, _("options --{raw,pairs} and --summary=only are mutually exclusive")); ++ ++ /* Shortcut to avoid scols machinery on --summary=only */ ++ if (lsmem->want_table == 0 && lsmem->want_summary) { ++ read_basic_info(lsmem); ++ read_info(lsmem); ++ print_summary(lsmem); ++ return EXIT_SUCCESS; ++ } ++ ++ /* ++ * Default columns ++ */ ++ if (!ncolumns) { ++ add_column(columns, ncolumns++, COL_RANGE); ++ add_column(columns, ncolumns++, COL_SIZE); ++ add_column(columns, ncolumns++, COL_STATE); ++ add_column(columns, ncolumns++, COL_REMOVABLE); ++ add_column(columns, ncolumns++, COL_BLOCK); ++ } ++ ++ if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), ++ (int *) &ncolumns, column_name_to_id) < 0) ++ return EXIT_FAILURE; ++ ++ /* ++ * Initialize output ++ */ ++ scols_init_debug(0); ++ ++ if (!(lsmem->table = scols_new_table())) ++ errx(EXIT_FAILURE, _("failed to initialize output table")); ++ scols_table_enable_raw(lsmem->table, lsmem->raw); ++ scols_table_enable_export(lsmem->table, lsmem->export); ++ scols_table_enable_noheadings(lsmem->table, lsmem->noheadings); ++ ++ for (i = 0; i < ncolumns; i++) { ++ struct coldesc *ci = get_column_desc(i); ++ if (!scols_table_new_column(lsmem->table, ci->name, ci->whint, ci->flags)) ++ err(EXIT_FAILURE, _("Failed to initialize output column")); ++ } ++ ++ if (splitarg) { ++ int split[ARRAY_SIZE(coldescs)] = { 0 }; ++ static size_t nsplits = 0; ++ ++ reset_split_policy(lsmem, 0); /* disable all */ ++ ++ if (strcasecmp(splitarg, "none") == 0) ++ ; ++ else if (string_add_to_idarray(splitarg, split, ARRAY_SIZE(split), ++ (int *) &nsplits, column_name_to_id) < 0) ++ return EXIT_FAILURE; ++ ++ for (i = 0; i < nsplits; i++) { ++ switch (split[i]) { ++ case COL_STATE: ++ lsmem->split_by_state = 1; ++ break; ++ case COL_NODE: ++ lsmem->split_by_node = 1; ++ break; ++ case COL_REMOVABLE: ++ lsmem->split_by_removable = 1; ++ break; ++ case COL_ZONES: ++ lsmem->split_by_zones = 1; ++ break; ++ } ++ } ++ } else ++ reset_split_policy(lsmem, 1); /* enable all */ ++ ++ /* ++ * Read data and print output ++ */ ++ read_basic_info(lsmem); ++ read_info(lsmem); ++ ++ if (lsmem->want_table) { ++ fill_scols_table(lsmem); ++ scols_print_table(lsmem->table); ++ ++ if (lsmem->want_summary) ++ fputc('\n', stdout); ++ } ++ ++ if (lsmem->want_summary) ++ print_summary(lsmem); ++ ++ scols_unref_table(lsmem->table); ++ return 0; ++} +-- +2.13.6 + diff --git a/SOURCES/0143-lsmem-make-split-optional-follow-output-by-default.patch b/SOURCES/0143-lsmem-make-split-optional-follow-output-by-default.patch new file mode 100644 index 0000000..ec94d77 --- /dev/null +++ b/SOURCES/0143-lsmem-make-split-optional-follow-output-by-default.patch @@ -0,0 +1,119 @@ +From 66d819dc82080e9dba609b3bfff45c14d7c3ba3c Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 3 Nov 2017 10:58:33 +0100 +Subject: [PATCH] lsmem: make --split optional, follow output by default + +Let's keep lsmem backwardly compatible (<=v2.30) and create ranges +according to the output columns by default. This default behavior may +be modified by --split command line option. + +Upstream: http://github.com/karelzak/util-linux/commit/96cbe362c034305e5f12a912b4247b3321420ee7 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1496421 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/lsmem.1 | 14 ++++++-------- + sys-utils/lsmem.c | 49 ++++++++++++++++++++++++++++++------------------- + 2 files changed, 36 insertions(+), 27 deletions(-) + +diff --git a/sys-utils/lsmem.1 b/sys-utils/lsmem.1 +index 3f5cd7d4b..bfe312bfc 100644 +--- a/sys-utils/lsmem.1 ++++ b/sys-utils/lsmem.1 +@@ -16,14 +16,12 @@ Always explicitly define expected columns by using the \fB\-\-output\fR option + together with a columns list in environments where a stable output is required. + + The \fBlsmem\fP command lists a new memory range always when the current memory +-block distinguish from the previous block by STATE, REMOVABLE, NODE or ZONES +-attribute. This default behavior is possible to override by the +-\fB\-\-split\fR option (e.g. \fBlsmem \-\-split=STATE,ZONES\fR). The special +-word "none" may be used to ignore all differences between memory blocks and to +-create as large as possible continuous ranges. The opposite semantic is +-\fB\-\-all\fR to list individual memory blocks. The default split policy is +-subject to change. Always explicitly use \fB\-\-split\fR in environments where +-a stable output is required. ++block distinguish from the previous block by some output column. This default ++behavior is possible to override by the \fB\-\-split\fR option (e.g. \fBlsmem ++\-\-split=ZONES\fR). The special word "none" may be used to ignore all ++differences between memory blocks and to create as large as possible continuous ++ranges. The opposite semantic is \fB\-\-all\fR to list individual memory ++blocks. + + Note that some output columns may provide inaccurate information if a split policy + forces \fBlsmem\fP to ignore diffrences in some attributes. For example if you +diff --git a/sys-utils/lsmem.c b/sys-utils/lsmem.c +index 34a2847af..aaf7374fc 100644 +--- a/sys-utils/lsmem.c ++++ b/sys-utils/lsmem.c +@@ -202,6 +202,32 @@ static inline void reset_split_policy(struct lsmem *l, int enable) + l->split_by_zones = enable; + } + ++static void set_split_policy(struct lsmem *l, int cols[], size_t ncols) ++{ ++ size_t i; ++ ++ reset_split_policy(l, 0); ++ ++ for (i = 0; i < ncols; i++) { ++ switch (cols[i]) { ++ case COL_STATE: ++ l->split_by_state = 1; ++ break; ++ case COL_NODE: ++ l->split_by_node = 1; ++ break; ++ case COL_REMOVABLE: ++ l->split_by_removable = 1; ++ break; ++ case COL_ZONES: ++ l->split_by_zones = 1; ++ break; ++ default: ++ break; ++ } ++ } ++} ++ + static void add_scols_line(struct lsmem *lsmem, struct memory_block *blk) + { + size_t i; +@@ -638,32 +664,17 @@ int main(int argc, char **argv) + int split[ARRAY_SIZE(coldescs)] = { 0 }; + static size_t nsplits = 0; + +- reset_split_policy(lsmem, 0); /* disable all */ +- + if (strcasecmp(splitarg, "none") == 0) + ; + else if (string_add_to_idarray(splitarg, split, ARRAY_SIZE(split), + (int *) &nsplits, column_name_to_id) < 0) + return EXIT_FAILURE; + +- for (i = 0; i < nsplits; i++) { +- switch (split[i]) { +- case COL_STATE: +- lsmem->split_by_state = 1; +- break; +- case COL_NODE: +- lsmem->split_by_node = 1; +- break; +- case COL_REMOVABLE: +- lsmem->split_by_removable = 1; +- break; +- case COL_ZONES: +- lsmem->split_by_zones = 1; +- break; +- } +- } ++ set_split_policy(lsmem, split, nsplits); ++ + } else +- reset_split_policy(lsmem, 1); /* enable all */ ++ /* follow output columns */ ++ set_split_policy(lsmem, columns, ncolumns); + + /* + * Read data and print output +-- +2.13.6 + diff --git a/SOURCES/0144-libmount-fix-access-utab-write-test.patch b/SOURCES/0144-libmount-fix-access-utab-write-test.patch new file mode 100644 index 0000000..50dccbd --- /dev/null +++ b/SOURCES/0144-libmount-fix-access-utab-write-test.patch @@ -0,0 +1,226 @@ +From 5d238002ff22241a7d04608102a2182aa74d2b69 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 8 Nov 2017 16:47:40 +0100 +Subject: [PATCH] libmount: fix access() utab write test + +The commit c08396c7691e1e6a04b6b45892e7e4612ceed8d7 replaces +open(O_CREATE) with ecaccess(). Unfortunately, another code depends on +the original behavior. + +* let's make utab when really necessary rather than in the try_write() test + +* __mnt_new_table_from_file() returns NULL if tab-file does not + exists. This is incorrect for tab_update.c stuff. We need empty table + in this case. + +* we can check /run/mount/ directory for write access if + eaccess(filename) return ENOENT (because file does not exist) + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1499760 +Upstream: http://github.com/karelzak/util-linux/commit/06ff935ec3ad2290025b555ff32b590680af565f +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libmount/src/mountP.h | 2 +- + libmount/src/tab_parse.c | 11 +++++++---- + libmount/src/tab_update.c | 9 +++++---- + libmount/src/utils.c | 42 +++++++++++++++++++++++++++++++----------- + 4 files changed, 44 insertions(+), 20 deletions(-) + +diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h +index 6cabcedeb..8b3f92e17 100644 +--- a/libmount/src/mountP.h ++++ b/libmount/src/mountP.h +@@ -278,7 +278,7 @@ struct libmnt_table { + struct list_head ents; /* list of entries (libmnt_fs) */ + }; + +-extern struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt); ++extern struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt, int empty_for_enoent); + + /* + * Tab file format +diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c +index 987e671fa..c629c67ad 100644 +--- a/libmount/src/tab_parse.c ++++ b/libmount/src/tab_parse.c +@@ -714,7 +714,7 @@ int mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname) + return __mnt_table_parse_dir(tb, dirname); + } + +-struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt) ++struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt, int empty_for_enoent) + { + struct libmnt_table *tb; + struct stat st; +@@ -723,7 +723,8 @@ struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt) + if (!filename) + return NULL; + if (stat(filename, &st)) +- return NULL; ++ return empty_for_enoent ? mnt_new_table() : NULL; ++ + tb = mnt_new_table(); + if (tb) { + tb->fmt = fmt; +@@ -748,8 +749,10 @@ struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt) + */ + struct libmnt_table *mnt_new_table_from_file(const char *filename) + { +- assert(filename); +- return __mnt_new_table_from_file(filename, MNT_FMT_GUESS); ++ if (!filename) ++ return NULL; ++ ++ return __mnt_new_table_from_file(filename, MNT_FMT_GUESS, 0); + } + + /** +diff --git a/libmount/src/tab_update.c b/libmount/src/tab_update.c +index 1e7f32be0..5f503cad7 100644 +--- a/libmount/src/tab_update.c ++++ b/libmount/src/tab_update.c +@@ -567,6 +567,7 @@ leave: + + unlink(uq); /* be paranoid */ + free(uq); ++ DBG(UPDATE, mnt_debug_h(upd, "%s: done [rc=%d]", upd->filename, rc)); + return rc; + } + +@@ -600,7 +601,7 @@ static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc) + return rc; + + tb = __mnt_new_table_from_file(upd->filename, +- upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); ++ upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB, 1); + if (tb) + rc = add_file_entry(tb, upd); + if (lc) +@@ -626,7 +627,7 @@ static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc + return rc; + + tb = __mnt_new_table_from_file(upd->filename, +- upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); ++ upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB, 1); + if (tb) { + struct libmnt_fs *rem = mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD); + if (rem) { +@@ -656,7 +657,7 @@ static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *l + return rc; + + tb = __mnt_new_table_from_file(upd->filename, +- upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); ++ upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB, 1); + if (tb) { + struct libmnt_fs *cur = mnt_table_find_target(tb, + mnt_fs_get_srcpath(upd->fs), MNT_ITER_BACKWARD); +@@ -693,7 +694,7 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock * + return rc; + + tb = __mnt_new_table_from_file(upd->filename, +- upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); ++ upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB, 1); + if (tb) { + struct libmnt_fs *cur = mnt_table_find_target(tb, + mnt_fs_get_target(fs), +diff --git a/libmount/src/utils.c b/libmount/src/utils.c +index a275d0a0e..fa33bd9a1 100644 +--- a/libmount/src/utils.c ++++ b/libmount/src/utils.c +@@ -651,18 +651,37 @@ done: + return rc; + } + +-static int try_write(const char *filename) ++static int try_write(const char *filename, const char *directory) + { + int rc = 0; + + if (!filename) + return -EINVAL; + ++ DBG(UTILS, mnt_debug("try write %s dir: %s", filename, directory)); ++ + #ifdef HAVE_EACCESS +- if (eaccess(filename, R_OK|W_OK) != 0) +- rc = -errno; +-#else ++ /* Try eaccess() first, because open() is overkill, may be monitored by ++ * audit and we don't want to fill logs by our checks... ++ */ ++ if (eaccess(filename, R_OK|W_OK) == 0) { ++ DBG(UTILS, mnt_debug(" access OK")); ++ return 0; ++ } else if (errno != ENOENT) { ++ DBG(UTILS, mnt_debug(" access FAILED")); ++ return -errno; ++ } else if (directory) { ++ /* file does not exist; try if directory is writable */ ++ if (eaccess(directory, R_OK|W_OK) != 0) ++ rc = -errno; ++ ++ DBG(UTILS, mnt_debug(" access %s [%s]", rc ? "FAILED" : "OK", directory)); ++ return rc; ++ } else ++#endif + { ++ DBG(UTILS, mnt_debug(" doing open-write test")); ++ + int fd = open(filename, O_RDWR|O_CREAT|O_CLOEXEC, + S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH); + if (fd < 0) +@@ -670,7 +689,6 @@ static int try_write(const char *filename) + else + close(fd); + } +-#endif + return rc; + } + +@@ -704,7 +722,7 @@ int mnt_has_regular_mtab(const char **mtab, int *writable) + /* file exist */ + if (S_ISREG(st.st_mode)) { + if (writable) +- *writable = !try_write(filename); ++ *writable = !try_write(filename, NULL); + return 1; + } + goto done; +@@ -712,7 +730,7 @@ int mnt_has_regular_mtab(const char **mtab, int *writable) + + /* try to create the file */ + if (writable) { +- *writable = !try_write(filename); ++ *writable = !try_write(filename, NULL); + if (*writable) + return 1; + } +@@ -750,7 +768,7 @@ int mnt_has_regular_utab(const char **utab, int *writable) + /* file exist */ + if (S_ISREG(st.st_mode)) { + if (writable) +- *writable = !try_write(filename); ++ *writable = !try_write(filename, NULL); + return 1; + } + goto done; /* it's not regular file */ +@@ -767,11 +785,13 @@ int mnt_has_regular_utab(const char **utab, int *writable) + rc = mkdir(dirname, S_IWUSR| + S_IRUSR|S_IRGRP|S_IROTH| + S_IXUSR|S_IXGRP|S_IXOTH); +- free(dirname); +- if (rc && errno != EEXIST) ++ if (rc && errno != EEXIST) { ++ free(dirname); + goto done; /* probably EACCES */ ++ } + +- *writable = !try_write(filename); ++ *writable = !try_write(filename, dirname); ++ free(dirname); + if (*writable) + return 1; + } +-- +2.13.6 + diff --git a/SOURCES/0145-login-add-LOGIN_PLAIN_PROMPT-to-login.defs.patch b/SOURCES/0145-login-add-LOGIN_PLAIN_PROMPT-to-login.defs.patch new file mode 100644 index 0000000..58c0242 --- /dev/null +++ b/SOURCES/0145-login-add-LOGIN_PLAIN_PROMPT-to-login.defs.patch @@ -0,0 +1,86 @@ +From a8427f41c91ac12f0b208fe7eaf4e535b8a56464 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 4 Dec 2017 12:31:29 +0100 +Subject: [PATCH 145/146] login: add LOGIN_PLAIN_PROMPT to login.defs + +We have command line option -H to disable hostname in login prompt. +Unfortunately, in same cases (e.g. telnetd) it's impossible to specify +login(1) command line options due to hardcoded execl()... + +This patch introduces LOGIN_PLAIN_PROMPT boolean for /etc/login.defs +to suppress hostname in the prompt. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1520906 +Upstream: http://github.com/karelzak/util-linux/commit/e6b32e7d1adf2a0c09743d71dfdbe2742c5884ac +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + login-utils/login.1 | 12 ++++++++++++ + login-utils/login.c | 12 +++++++++--- + 2 files changed, 21 insertions(+), 3 deletions(-) + +diff --git a/login-utils/login.1 b/login-utils/login.1 +index 216645ebe..ec636fd74 100644 +--- a/login-utils/login.1 ++++ b/login-utils/login.1 +@@ -130,6 +130,9 @@ Used by other servers (i.e., + to tell + .B login + that printing the hostname should be suppressed in the login: prompt. ++See also LOGIN_PLAIN_PROMPT below if your server does not allow to configure ++.B login ++command line. + .TP + .B \-V + Print version and exit. +@@ -157,6 +160,15 @@ by + PAM module. + .RE + .PP ++.B LOGIN_PLAIN_PROMPT ++(boolean) ++.RS 4 ++Tell login that printing the hostname should be suppressed in the login: ++prompt. This is alternative to the \fB\-H\fR command line option. The default ++value is ++.IR no . ++.RE ++.PP + .B LOGIN_TIMEOUT + (number) + .RS 4 +diff --git a/login-utils/login.c b/login-utils/login.c +index 5c36953ef..eee3f7bd1 100644 +--- a/login-utils/login.c ++++ b/login-utils/login.c +@@ -709,7 +709,8 @@ static void loginpam_err(pam_handle_t *pamh, int retcode) + } + + /* +- * Composes "<host> login: " string; or returns "login: " is -H is given ++ * Composes "<host> login: " string; or returns "login: " if -H is given or ++ * LOGIN_PLAIN_PROMPT=yes configured. + */ + static const char *loginpam_get_prompt(struct login_context *cxt) + { +@@ -717,11 +718,16 @@ static const char *loginpam_get_prompt(struct login_context *cxt) + char *prompt, *dflt_prompt = _("login: "); + size_t sz; + +- if (cxt->nohost || !(host = get_thishost(cxt, NULL))) ++ if (cxt->nohost) ++ return dflt_prompt; /* -H on command line */ ++ ++ if (getlogindefs_bool("LOGIN_PLAIN_PROMPT", 0) == 1) + return dflt_prompt; + +- sz = strlen(host) + 1 + strlen(dflt_prompt) + 1; ++ if (!(host = get_thishost(cxt, NULL))) ++ return dflt_prompt; + ++ sz = strlen(host) + 1 + strlen(dflt_prompt) + 1; + prompt = xmalloc(sz); + snprintf(prompt, sz, "%s %s", host, dflt_prompt); + +-- +2.13.6 + diff --git a/SOURCES/0146-libblkid-Add-VDO-superblock-information-into-blkid.patch b/SOURCES/0146-libblkid-Add-VDO-superblock-information-into-blkid.patch new file mode 100644 index 0000000..22ee16c --- /dev/null +++ b/SOURCES/0146-libblkid-Add-VDO-superblock-information-into-blkid.patch @@ -0,0 +1,114 @@ +From e8c8b5f97c864f0fd65378ccde0c45526e6916c9 Mon Sep 17 00:00:00 2001 +From: Sweet Tea Dorminy <sweettea@mit.edu> +Date: Wed, 6 Dec 2017 18:26:59 -0500 +Subject: [PATCH 146/146] libblkid: Add VDO superblock information into blkid + +[kzak@redhat.com: - add tests/expected/blkid/low-probe-vdo + - enlarge the image (must be > 1024)] + +[RHEL7: exclude vdo regression test doe to binary stuff in the patch] + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1521163 +Upstream: http://github.com/karelzak/util-linux/commit/6418cba457a701d44294d934c2bc4b766bbcfa2b +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/Makemodule.am | 1 + + libblkid/src/superblocks/superblocks.c | 1 + + libblkid/src/superblocks/superblocks.h | 1 + + libblkid/src/superblocks/vdo.c | 48 +++++++++++++++++++++++++++++++++ + 4 files changed, 55 insertions(+) + create mode 100644 libblkid/src/superblocks/vdo.c + +diff --git a/libblkid/src/Makemodule.am b/libblkid/src/Makemodule.am +index 15639768a..791d7cbe8 100644 +--- a/libblkid/src/Makemodule.am ++++ b/libblkid/src/Makemodule.am +@@ -88,6 +88,7 @@ libblkid_la_SOURCES = \ + libblkid/src/superblocks/ubifs.c \ + libblkid/src/superblocks/udf.c \ + libblkid/src/superblocks/ufs.c \ ++ libblkid/src/superblocks/vdo.c \ + libblkid/src/superblocks/vfat.c \ + libblkid/src/superblocks/via_raid.c \ + libblkid/src/superblocks/vmfs.c \ +diff --git a/libblkid/src/superblocks/superblocks.c b/libblkid/src/superblocks/superblocks.c +index 3721544ff..8210fc17b 100644 +--- a/libblkid/src/superblocks/superblocks.c ++++ b/libblkid/src/superblocks/superblocks.c +@@ -109,6 +109,7 @@ static const struct blkid_idinfo *idinfos[] = + &verity_hash_idinfo, + &luks_idinfo, + &vmfs_volume_idinfo, ++ &vdo_idinfo, + + /* Filesystems */ + &vfat_idinfo, +diff --git a/libblkid/src/superblocks/superblocks.h b/libblkid/src/superblocks/superblocks.h +index 90847151b..487fe38c2 100644 +--- a/libblkid/src/superblocks/superblocks.h ++++ b/libblkid/src/superblocks/superblocks.h +@@ -72,6 +72,7 @@ extern const struct blkid_idinfo befs_idinfo; + extern const struct blkid_idinfo nilfs2_idinfo; + extern const struct blkid_idinfo exfat_idinfo; + extern const struct blkid_idinfo f2fs_idinfo; ++extern const struct blkid_idinfo vdo_idinfo; + + /* + * superblock functions +diff --git a/libblkid/src/superblocks/vdo.c b/libblkid/src/superblocks/vdo.c +new file mode 100644 +index 000000000..bec686f4f +--- /dev/null ++++ b/libblkid/src/superblocks/vdo.c +@@ -0,0 +1,48 @@ ++/* ++ * Copyright (C) 2017 Red Hat, Inc. ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++ ++#include <stdio.h> ++#include <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <errno.h> ++#include <ctype.h> ++#include <stdint.h> ++ ++#include "superblocks.h" ++ ++struct vdo_super_block { ++ char magic[8]; /* magic number 'dmvdo001'*/ ++ char unused[32]; /* 32 bytes of unimportant space */ ++ unsigned char sb_uuid[16]; /* vdo unique id */ ++ ++ /* this is not all... but enough for libblkid */ ++} __attribute__((packed)); ++ ++static int probe_vdo(blkid_probe pr, const struct blkid_idmag *mag) ++{ ++ struct vdo_super_block *vsb; ++ ++ vsb = blkid_probe_get_sb(pr, mag, struct vdo_super_block); ++ if (!vsb) ++ return errno ? -errno : 1; ++ ++ blkid_probe_set_uuid(pr, vsb->sb_uuid); ++ return 0; ++} ++ ++const struct blkid_idinfo vdo_idinfo = ++{ ++ .name = "vdo", ++ .usage = BLKID_USAGE_OTHER, ++ .probefunc = probe_vdo, ++ .magics = ++ { ++ { .magic = "dmvdo001", .len = 8 }, ++ { NULL } ++ } ++}; +-- +2.13.6 + diff --git a/SOURCES/0147-include-debug-don-t-print-pointer-address-for-SUID-p.patch b/SOURCES/0147-include-debug-don-t-print-pointer-address-for-SUID-p.patch new file mode 100644 index 0000000..296753c --- /dev/null +++ b/SOURCES/0147-include-debug-don-t-print-pointer-address-for-SUID-p.patch @@ -0,0 +1,406 @@ +From 3fcd52706b6818e785a104ed6c4f2b46e5d1ab2f Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 12 Jan 2018 11:01:26 +0100 +Subject: [PATCH] include/debug: don't print pointer address for SUID programs + +* introduce new flag __UL_DEBUG_FL_NOADDR to suppress pointer address printing + (and MNT_DEBUG_FL_NOADDR for libmount) + +* use __UL_DEBUG_FL_NOADDR when SUID + +* move ul_debugobj() to debugobj.h, and require UL_DEBUG_CURRENT_MASK + to provide access to the current mask from ul_debugobj(). It's better + than modify all ul_debugobj() calls and use the global mask as + argument. + +* remove never used UL_DEBUG_DEFINE_FLAG + +* remove %p from another libmount and libblkid debug messages + +Upstream: http://github.com/karelzak/util-linux/commit/6d00cfb2330cb47d00d350eedfbffbbf5991a743 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1534893 +Reported-by: halfdog <me@halfdog.net> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + include/Makemodule.am | 2 ++ + include/debug.h | 29 +++++++++++------------------ + include/debugobj.h | 22 ++++++++++++++++++++++ + lib/loopdev.c | 2 +- + libblkid/src/partitions/partitions.c | 20 ++++++++++---------- + libblkid/src/probe.c | 21 ++++++++++----------- + libmount/src/fs.c | 2 +- + libmount/src/init.c | 5 +++++ + libmount/src/mountP.h | 4 +++- + libmount/src/tab_diff.c | 6 +++--- + libmount/src/tab_update.c | 4 ++-- + libsmartcols/src/smartcolsP.h | 3 +++ + sys-utils/lsns.c | 3 +++ + 13 files changed, 76 insertions(+), 47 deletions(-) + create mode 100644 include/debugobj.h + +diff --git a/include/Makemodule.am b/include/Makemodule.am +index 168029683..bd4aa8cea 100644 +--- a/include/Makemodule.am ++++ b/include/Makemodule.am +@@ -11,6 +11,8 @@ dist_noinst_HEADERS += \ + include/colors.h \ + include/cpuset.h \ + include/crc32.h \ ++ include/debug.h \ ++ include/debugobj.h \ + include/env.h \ + include/exec_shell.h \ + include/exitcodes.h \ +diff --git a/include/debug.h b/include/debug.h +index 848e47456..1c7ed8037 100644 +--- a/include/debug.h ++++ b/include/debug.h +@@ -13,12 +13,15 @@ + 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_MASK(m) m ## _debug_mask ++#define UL_DEBUG_DEFINE_MASK(m) int UL_DEBUG_MASK(m) + #define UL_DEBUG_DECLARE_MASK(m) extern UL_DEBUG_DEFINE_MASK(m) + #define UL_DEBUG_DEFINE_MASKNAMES(m) static const struct dbg_mask m ## _masknames[] + +-/* p - flag prefix, m - flag postfix */ +-#define UL_DEBUG_DEFINE_FLAG(p, m) p ## m ++/* ++ * Internal mask flags (above 0xffffff) ++ */ ++#define __UL_DEBUG_FL_NOADDR (1 << 24) /* Don't print object address */ + + /* l - library name, p - flag prefix, m - flag postfix, x - function */ + #define __UL_DBG(l, p, m, x) \ +@@ -55,6 +58,10 @@ struct dbg_mask { char *mname; int val; }; + lib ## _debug_mask = parse_envmask(lib ## _masknames, str); \ + } else \ + lib ## _debug_mask = mask; \ ++ if (lib ## _debug_mask) { \ ++ if (getuid() != geteuid() || getgid() != getegid()) \ ++ lib ## _debug_mask |= __UL_DEBUG_FL_NOADDR; \ ++ } \ + lib ## _debug_mask |= pref ## INIT; \ + if (lib ## _debug_mask != pref ## INIT) { \ + __UL_DBG(lib, pref, INIT, ul_debug("library debug mask: 0x%04x", \ +@@ -72,21 +79,7 @@ ul_debug(const char *mesg, ...) + 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[], ++static inline int parse_envmask(const struct dbg_mask flagnames[], + const char *mask) + { + int res; +diff --git a/include/debugobj.h b/include/debugobj.h +new file mode 100644 +index 000000000..73b70b8df +--- /dev/null ++++ b/include/debugobj.h +@@ -0,0 +1,22 @@ ++#ifndef UTIL_LINUX_DEBUGOBJ_H ++#define UTIL_LINUX_DEBUGOBJ_H ++ ++/* ++ * Include *after* debug.h and after UL_DEBUG_CURRENT_MASK define. ++ */ ++ ++static inline void __attribute__ ((__format__ (__printf__, 2, 3))) ++ul_debugobj(const void *handler, const char *mesg, ...) ++{ ++ va_list ap; ++ ++ if (handler && !(UL_DEBUG_CURRENT_MASK & __UL_DEBUG_FL_NOADDR)) ++ fprintf(stderr, "[%p]: ", handler); ++ ++ va_start(ap, mesg); ++ vfprintf(stderr, mesg, ap); ++ va_end(ap); ++ fputc('\n', stderr); ++} ++ ++#endif /* UTIL_LINUX_DEBUGOBJ_H */ +diff --git a/lib/loopdev.c b/lib/loopdev.c +index db5463698..daf0a81e8 100644 +--- a/lib/loopdev.c ++++ b/lib/loopdev.c +@@ -50,7 +50,7 @@ + + # define DBG(l,x) do { \ + if ((l)->debug) {\ +- fprintf(stderr, "loopdev: [%p]: ", (l)); \ ++ fprintf(stderr, "loopdev: "); \ + x; \ + } \ + } while(0) +diff --git a/libblkid/src/partitions/partitions.c b/libblkid/src/partitions/partitions.c +index 9d846ff85..2d0d70d81 100644 +--- a/libblkid/src/partitions/partitions.c ++++ b/libblkid/src/partitions/partitions.c +@@ -381,8 +381,8 @@ static blkid_partlist partitions_init_data(struct blkid_chain *chn) + + reset_partlist(ls); + +- DBG(LOWPROBE, blkid_debug("parts: initialized partitions list (%p, size=%d)", +- ls, ls->nparts_max)); ++ DBG(LOWPROBE, blkid_debug("parts: initialized partitions list (size=%d)", ++ ls->nparts_max)); + return ls; + } + +@@ -417,7 +417,7 @@ blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls, + list_add_tail(&tab->t_tabs, &ls->l_tabs); + + DBG(LOWPROBE, blkid_debug("parts: create a new partition table " +- "(%p, type=%s, offset=%"PRId64")", tab, type, offset)); ++ "(type=%s, offset=%"PRId64")", type, offset)); + return tab; + } + +@@ -458,9 +458,9 @@ blkid_partition blkid_partlist_add_partition(blkid_partlist ls, + par->start = start; + par->size = size; + +- DBG(LOWPROBE, blkid_debug("parts: add partition (%p start=%" +- PRId64 ", size=%" PRId64 ", table=%p)", +- par, par->start, par->size, tab)); ++ DBG(LOWPROBE, blkid_debug("parts: add partition (start=%" ++ PRId64 ", size=%" PRId64 ")", ++ par->start, par->size)); + return par; + } + +@@ -662,8 +662,8 @@ int blkid_partitions_do_subprobe(blkid_probe pr, blkid_partition parent, + blkid_loff_t sz, off; + + DBG(LOWPROBE, blkid_debug( +- "parts: ----> %s subprobe requested (parent=%p)", +- id->name, parent)); ++ "parts: ----> %s subprobe requested)", ++ id->name)); + + if (!pr || !parent || !parent->size) + return -EINVAL; +@@ -709,8 +709,8 @@ int blkid_partitions_do_subprobe(blkid_probe pr, blkid_partition parent, + blkid_free_probe(prc); /* free cloned prober */ + + DBG(LOWPROBE, blkid_debug( +- "parts: <---- %s subprobe done (parent=%p, rc=%d)", +- id->name, parent, rc)); ++ "parts: <---- %s subprobe done (rc=%d)", ++ id->name, rc)); + + return rc; + } +diff --git a/libblkid/src/probe.c b/libblkid/src/probe.c +index 9cf099ae4..07b08441f 100644 +--- a/libblkid/src/probe.c ++++ b/libblkid/src/probe.c +@@ -145,7 +145,7 @@ blkid_probe blkid_new_probe(void) + if (!pr) + return NULL; + +- DBG(LOWPROBE, blkid_debug("allocate a new probe %p", pr)); ++ DBG(LOWPROBE, blkid_debug("allocate a new probe")); + + /* initialize chains */ + for (i = 0; i < BLKID_NCHAINS; i++) { +@@ -260,7 +260,7 @@ void blkid_free_probe(blkid_probe pr) + blkid_probe_reset_buffer(pr); + blkid_free_probe(pr->disk_probe); + +- DBG(LOWPROBE, blkid_debug("free probe %p", pr)); ++ DBG(LOWPROBE, blkid_debug("free probe")); + free(pr); + } + +@@ -552,8 +552,8 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, + list_entry(p, struct blkid_bufinfo, bufs); + + if (x->off <= off && off + len <= x->off + x->len) { +- DBG(LOWPROBE, blkid_debug("\treuse buffer: off=%jd len=%jd pr=%p", +- x->off, x->len, pr)); ++ DBG(LOWPROBE, blkid_debug("\treuse buffer: off=%jd len=%jd", ++ x->off, x->len)); + bf = x; + break; + } +@@ -584,8 +584,8 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, + bf->off = off; + INIT_LIST_HEAD(&bf->bufs); + +- DBG(LOWPROBE, blkid_debug("\tbuffer read: off=%jd len=%jd pr=%p", +- off, len, pr)); ++ DBG(LOWPROBE, blkid_debug("\tbuffer read: off=%jd len=%jd", ++ off, len)); + + ret = read(pr->fd, bf->data, len); + if (ret != (ssize_t) len) { +@@ -609,7 +609,7 @@ static void blkid_probe_reset_buffer(blkid_probe pr) + if (!pr || list_empty(&pr->buffers)) + return; + +- DBG(LOWPROBE, blkid_debug("reseting probing buffers pr=%p", pr)); ++ DBG(LOWPROBE, blkid_debug("reseting probing buffers")); + + while (!list_empty(&pr->buffers)) { + struct blkid_bufinfo *bf = list_entry(pr->buffers.next, +@@ -766,9 +766,8 @@ int blkid_probe_set_dimension(blkid_probe pr, + return -1; + + DBG(LOWPROBE, blkid_debug( +- "changing probing area pr=%p: size=%llu, off=%llu " ++ "changing probing area: size=%llu, off=%llu " + "-to-> size=%llu, off=%llu", +- pr, + (unsigned long long) pr->size, + (unsigned long long) pr->off, + (unsigned long long) size, +@@ -840,7 +839,7 @@ int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id, + static inline void blkid_probe_start(blkid_probe pr) + { + if (pr) { +- DBG(LOWPROBE, blkid_debug("%p: start probe", pr)); ++ DBG(LOWPROBE, blkid_debug("start probe")); + pr->cur_chain = NULL; + pr->prob_flags = 0; + blkid_probe_set_wiper(pr, 0, 0); +@@ -850,7 +849,7 @@ static inline void blkid_probe_start(blkid_probe pr) + static inline void blkid_probe_end(blkid_probe pr) + { + if (pr) { +- DBG(LOWPROBE, blkid_debug("%p: end probe", pr)); ++ DBG(LOWPROBE, blkid_debug("end probe")); + pr->cur_chain = NULL; + pr->prob_flags = 0; + blkid_probe_set_wiper(pr, 0, 0); +diff --git a/libmount/src/fs.c b/libmount/src/fs.c +index 75e3bbb26..e46ee0c0e 100644 +--- a/libmount/src/fs.c ++++ b/libmount/src/fs.c +@@ -1451,7 +1451,7 @@ int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file) + { + if (!fs || !file) + return -EINVAL; +- fprintf(file, "------ fs: %p\n", fs); ++ fprintf(file, "------ fs\n"); + fprintf(file, "source: %s\n", mnt_fs_get_source(fs)); + fprintf(file, "target: %s\n", mnt_fs_get_target(fs)); + fprintf(file, "fstype: %s\n", mnt_fs_get_fstype(fs)); +diff --git a/libmount/src/init.c b/libmount/src/init.c +index 4e5f489c4..e5e6925f5 100644 +--- a/libmount/src/init.c ++++ b/libmount/src/init.c +@@ -38,6 +38,11 @@ void mnt_init_debug(int mask) + } else + libmount_debug_mask = mask; + ++ if (libmount_debug_mask) { ++ if (getuid() != geteuid() || getgid() != getegid()) ++ libmount_debug_mask |= MNT_DEBUG_FL_NOADDR; ++ } ++ + libmount_debug_mask |= MNT_DEBUG_INIT; + + if (libmount_debug_mask && libmount_debug_mask != MNT_DEBUG_INIT) { +diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h +index 8b3f92e17..dc3ed3f49 100644 +--- a/libmount/src/mountP.h ++++ b/libmount/src/mountP.h +@@ -51,6 +51,8 @@ + #define MNT_DEBUG_DIFF (1 << 11) + #define MNT_DEBUG_ALL 0xFFFF + ++#define MNT_DEBUG_FL_NOADDR (1 << 24) ++ + #ifdef CONFIG_LIBMOUNT_DEBUG + # include <stdio.h> + # include <stdarg.h> +@@ -91,7 +93,7 @@ mnt_debug_h(void *handler, const char *mesg, ...) + { + va_list ap; + +- if (handler) ++ if (handler && !(libmount_debug_mask & MNT_DEBUG_FL_NOADDR)) + fprintf(stderr, "[%p]: ", handler); + va_start(ap, mesg); + vfprintf(stderr, mesg, ap); +diff --git a/libmount/src/tab_diff.c b/libmount/src/tab_diff.c +index f01f889f8..0a69f402c 100644 +--- a/libmount/src/tab_diff.c ++++ b/libmount/src/tab_diff.c +@@ -229,9 +229,9 @@ int mnt_diff_tables(struct libmnt_tabdiff *df, struct libmnt_table *old_tab, + if (!no && !nn) /* both tables are empty */ + return 0; + +- DBG(DIFF, mnt_debug_h(df, "analyze new=%p (%d entries), " +- "old=%p (%d entries)", +- new_tab, nn, old_tab, no)); ++ DBG(DIFF, mnt_debug_h(df, "analyze new (%d entries), " ++ "old (%d entries)", ++ nn, no)); + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + +diff --git a/libmount/src/tab_update.c b/libmount/src/tab_update.c +index 5f503cad7..b45c4a92c 100644 +--- a/libmount/src/tab_update.c ++++ b/libmount/src/tab_update.c +@@ -173,8 +173,8 @@ int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags, + return -EINVAL; + + DBG(UPDATE, mnt_debug_h(upd, +- "resetting FS [fs=0x%p, target=%s, flags=0x%08lx]", +- fs, target, mountflags)); ++ "resetting FS [target=%s, flags=0x%08lx]", ++ target, mountflags)); + if (fs) { + DBG(UPDATE, mnt_debug_h(upd, "FS template:")); + DBG(UPDATE, mnt_fs_print_debug(fs, stderr)); +diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h +index 28246c14f..cea4f3101 100644 +--- a/libsmartcols/src/smartcolsP.h ++++ b/libsmartcols/src/smartcolsP.h +@@ -43,6 +43,9 @@ UL_DEBUG_DECLARE_MASK(libsmartcols); + #define ON_DBG(m, x) __UL_DBG_CALL(libsmartcols, SCOLS_DEBUG_, m, x) + #define DBG_FLUSH __UL_DBG_FLUSH(libsmartcols, SCOLS_DEBUG_) + ++#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(libsmartcols) ++#include "debugobj.h" ++ + /* + * Generic iterator + */ +diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c +index 5ee298172..fb53a16a4 100644 +--- a/sys-utils/lsns.c ++++ b/sys-utils/lsns.c +@@ -55,6 +55,9 @@ UL_DEBUG_DEFINE_MASKNAMES(lsns) = UL_DEBUG_EMPTY_MASKNAMES; + #define DBG(m, x) __UL_DBG(lsns, LSNS_DEBUG_, m, x) + #define ON_DBG(m, x) __UL_DBG_CALL(lsns, LSNS_DEBUG_, m, x) + ++#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(lsns) ++#include "debugobj.h" ++ + struct idcache *uid_cache = NULL; + + /* column IDs */ +-- +2.13.6 + diff --git a/SOURCES/0148-mkswap-tolerate-ENOTSUP-when-failing-to-relabel.patch b/SOURCES/0148-mkswap-tolerate-ENOTSUP-when-failing-to-relabel.patch new file mode 100644 index 0000000..d1f36df --- /dev/null +++ b/SOURCES/0148-mkswap-tolerate-ENOTSUP-when-failing-to-relabel.patch @@ -0,0 +1,34 @@ +From a656eda4cf268f555563d45808df4bb1df54c95f Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel <lkundrak@v3.sk> +Date: Mon, 18 Apr 2016 09:01:23 +0200 +Subject: [PATCH] mkswap: tolerate ENOTSUP when failing to relabel + +It might be that the underlying filesystem just doesn't support SELinux +labeling. This fixes creating swap on vfat live media: + + # livecd-iso-to-disk.sh --msdos --swap-size-mb 666 ... + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1538545 +Upstream: http://github.com/karelzak/util-linux/commit/d97dc0ee2505e80c8e9fca89aa2001b2ec2c3695 +Signed-off-by: Lubomir Rintel <lkundrak@v3.sk> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + disk-utils/mkswap.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/disk-utils/mkswap.c b/disk-utils/mkswap.c +index ec6fc5f99..ff47f2038 100644 +--- a/disk-utils/mkswap.c ++++ b/disk-utils/mkswap.c +@@ -656,7 +656,7 @@ main(int argc, char **argv) { + context_string = context_str(newcontext); + + if (strcmp(context_string, oldcontext)!=0) { +- if (fsetfilecon(DEV, context_string)) ++ if (fsetfilecon(DEV, context_string) && errno != ENOTSUP) + err(EXIT_FAILURE, _("unable to relabel %s to %s"), + device_name, context_string); + } +-- +2.13.6 + diff --git a/SOURCES/0149-libmount-fix-debug-message.patch b/SOURCES/0149-libmount-fix-debug-message.patch new file mode 100644 index 0000000..c3e2be7 --- /dev/null +++ b/SOURCES/0149-libmount-fix-debug-message.patch @@ -0,0 +1,28 @@ +From c817652429d4326bb43a8d975611927170447805 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 1 Feb 2018 16:52:14 +0100 +Subject: [PATCH] libmount: fix debug message + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1534893 +Upstream: http://github.com/karelzak/util-linux/commit/b058f473d175450c48a65031f1abc8d8b90b2b7b +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libmount/src/fs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libmount/src/fs.c b/libmount/src/fs.c +index e46ee0c0e..e5c38c1b6 100644 +--- a/libmount/src/fs.c ++++ b/libmount/src/fs.c +@@ -1451,7 +1451,7 @@ int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file) + { + if (!fs || !file) + return -EINVAL; +- fprintf(file, "------ fs\n"); ++ fprintf(file, "------ fs:\n"); + fprintf(file, "source: %s\n", mnt_fs_get_source(fs)); + fprintf(file, "target: %s\n", mnt_fs_get_target(fs)); + fprintf(file, "fstype: %s\n", mnt_fs_get_fstype(fs)); +-- +2.14.3 + diff --git a/SOURCES/0150-lsns-missing-ns-name-is-not-error.patch b/SOURCES/0150-lsns-missing-ns-name-is-not-error.patch new file mode 100644 index 0000000..33bf09e --- /dev/null +++ b/SOURCES/0150-lsns-missing-ns-name-is-not-error.patch @@ -0,0 +1,44 @@ +From 8694f63002c6b765e72c36fbf8ed46164d5303e5 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 15 Aug 2016 11:02:18 +0200 +Subject: [PATCH 150/173] lsns: missing ns/<name> is not error +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For example user namespace is optional it does not make sense to +ignore process completely if the ns/user file is missing. + +Reported-by: Michał Bartoszkiewicz <mbartoszkiewicz@gmail.com> +Upstream: http://github.com/karelzak/util-linux/commit/3082f8518f2739e9f68e660f1749acdd2b9d7a97 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1543428 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/lsns.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c +index fb53a16a4..b8841b7a3 100644 +--- a/sys-utils/lsns.c ++++ b/sys-utils/lsns.c +@@ -204,7 +204,7 @@ static inline const struct colinfo *get_column_info(unsigned num) + return &infos[ get_column_id(num) ]; + } + +-static ino_t get_ns_ino(int dir, const char *nsname, ino_t *ino) ++static int get_ns_ino(int dir, const char *nsname, ino_t *ino) + { + struct stat st; + char path[16]; +@@ -269,7 +269,7 @@ static int read_process(struct lsns *ls, pid_t pid) + continue; + + rc = get_ns_ino(dirfd(dir), ns_names[i], &p->ns_ids[i]); +- if (rc && rc != -EACCES) ++ if (rc && rc != -EACCES && rc != -ENOENT) + goto done; + rc = 0; + } +-- +2.14.4 + diff --git a/SOURCES/0151-lsns-Fix-parser-for-proc-pid-stat-which-is-including.patch b/SOURCES/0151-lsns-Fix-parser-for-proc-pid-stat-which-is-including.patch new file mode 100644 index 0000000..bad3a76 --- /dev/null +++ b/SOURCES/0151-lsns-Fix-parser-for-proc-pid-stat-which-is-including.patch @@ -0,0 +1,81 @@ +From 7a151a3d74b2972410103b684803e6d6b8fda15b Mon Sep 17 00:00:00 2001 +From: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> +Date: Wed, 23 Nov 2016 14:13:34 +0900 +Subject: [PATCH 151/173] lsns: Fix parser for /proc/<pid>/stat which is + including space in comm + +For example, child process of spamd has + + 32031 (spamd child) S 32026 32026 32026 0 -1 4210752 338 0 0 0 ... + +fscanf("%d %*s %c %d*[^\n]") in read_process() can't parse above as we +expected, because %s only skips non-whitespace. I.e. it parses like +following, + + 32031 (spamd child) S 32026 32026 32026 0 -1 4210752 338 0 0 0 ... + +---+ +----+ + + %d %*s %c + +and returns 2 (pid=32031, state=c). + +This fixes it by skipping task->comm part manually. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1543428 +Upstream: http://github.com/karelzak/util-linux/commit/3fcbd7978980dc1a29c626b701333e27599e506d +Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> +--- + sys-utils/lsns.c | 30 ++++++++++++++++++++++++++---- + 1 file changed, 26 insertions(+), 4 deletions(-) + +diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c +index b8841b7a3..d32756508 100644 +--- a/sys-utils/lsns.c ++++ b/sys-utils/lsns.c +@@ -217,6 +217,30 @@ static int get_ns_ino(int dir, const char *nsname, ino_t *ino) + return 0; + } + ++static int parse_proc_stat(FILE *fp, pid_t *pid, char *state, pid_t *ppid) ++{ ++ char *line = NULL, *p; ++ size_t len = 0; ++ int rc; ++ ++ if (getline(&line, &len, fp) < 0) { ++ rc = -errno; ++ goto error; ++ } ++ ++ p = strrchr(line, ')'); ++ if (p == NULL || ++ sscanf(line, "%d (", pid) != 1 || ++ sscanf(p, ") %c %d*[^\n]", state, ppid) != 2) { ++ rc = -EINVAL; ++ goto error; ++ } ++ rc = 0; ++ ++error: ++ free(line); ++ return rc; ++} + + static int read_process(struct lsns *ls, pid_t pid) + { +@@ -255,11 +279,9 @@ static int read_process(struct lsns *ls, pid_t pid) + rc = -errno; + goto done; + } +- rc = fscanf(f, "%d %*s %c %d*[^\n]", &p->pid, &p->state, &p->ppid); +- if (rc != 3) { +- rc = rc < 0 ? -errno : -EINVAL; ++ rc = parse_proc_stat(f, &p->pid, &p->state, &p->ppid); ++ if (rc < 0) + goto done; +- } + rc = 0; + + for (i = 0; i < ARRAY_SIZE(p->ns_ids); i++) { +-- +2.14.4 + diff --git a/SOURCES/0152-libsmartcols-add-basic-tools-necessary-for-new-versi.patch b/SOURCES/0152-libsmartcols-add-basic-tools-necessary-for-new-versi.patch new file mode 100644 index 0000000..31db3c6 --- /dev/null +++ b/SOURCES/0152-libsmartcols-add-basic-tools-necessary-for-new-versi.patch @@ -0,0 +1,757 @@ +From c465ce9765273e8e1227b192e1917826ac4eaaf7 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 31 May 2018 11:13:31 +0200 +Subject: [PATCH 152/173] libsmartcols: add basic tools necessary for new + version + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1561350 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + configure.ac | 3 + + include/carefulputc.h | 96 +++++++++++++++++++++++++++- + include/colors.h | 5 ++ + include/mbsalign.h | 9 ++- + include/strutils.h | 13 ++-- + include/ttyutils.h | 1 + + lib/Makemodule.am | 1 + + lib/color-names.c | 57 +++++++++++++++++ + lib/mbsalign.c | 172 +++++++++++++++++++++++++++++++++++++++++--------- + lib/ttyutils.c | 52 +++++++++++++++ + libfdisk/src/ask.c | 4 +- + 11 files changed, 373 insertions(+), 40 deletions(-) + create mode 100644 lib/color-names.c + +diff --git a/configure.ac b/configure.ac +index d561e01d0..8cf317dc0 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -133,6 +133,9 @@ AC_SUBST([BSD_WARN_CFLAGS]) + dnl libtool-2 + LT_INIT + ++dnl check supported linker flags ++AX_CHECK_VSCRIPT ++ + m4_ifndef([PKG_PROG_PKG_CONFIG], + [m4_fatal([Could not locate the pkg-config autoconf + macros. These are usually located in /usr/share/aclocal/pkg.m4. +diff --git a/include/carefulputc.h b/include/carefulputc.h +index a54498cfd..613d94c1e 100644 +--- a/include/carefulputc.h ++++ b/include/carefulputc.h +@@ -26,7 +26,87 @@ static inline int carefulputc(int c, FILE *fp) { + return (ret < 0) ? EOF : 0; + } + +-static inline void fputs_quoted(const char *data, FILE *out) ++/* ++ * Backported for RHEL7.6 libsmartcols ++ */ ++ ++/* ++ * Requirements enumerated via testing (V8, Firefox, IE11): ++ * ++ * var charsToEscape = []; ++ * for (var i = 0; i < 65535; i += 1) { ++ * try { ++ * JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}'); ++ * } catch (e) { ++ * charsToEscape.push(i); ++ * } ++ * } ++ */ ++static inline void fputs_quoted_case_json(const char *data, FILE *out, int dir) ++{ ++ const char *p; ++ ++ fputc('"', out); ++ for (p = data; p && *p; p++) { ++ ++ const unsigned char c = (unsigned char) *p; ++ ++ /* From http://www.json.org ++ * ++ * The double-quote and backslashes would break out a string or ++ * init an escape sequence if not escaped. ++ * ++ * Note that single-quotes and forward slashes, while they're ++ * in the JSON spec, don't break double-quoted strings. ++ */ ++ if (c == '"' || c == '\\') { ++ fputc('\\', out); ++ fputc(c, out); ++ continue; ++ } ++ ++ /* All non-control characters OK; do the case swap as required. */ ++ if (c >= 0x20) { ++ fputc(dir == 1 ? toupper(c) : ++ dir == -1 ? tolower(c) : *p, out); ++ continue; ++ } ++ ++ /* In addition, all chars under ' ' break Node's/V8/Chrome's, and ++ * Firefox's JSON.parse function ++ */ ++ switch (c) { ++ /* Handle short-hand cases to reduce output size. C ++ * has most of the same stuff here, so if there's an ++ * "Escape for C" function somewhere in the STL, we ++ * should probably be using it. ++ */ ++ case '\b': ++ fputs("\\b", out); ++ break; ++ case '\t': ++ fputs("\\t", out); ++ break; ++ case '\n': ++ fputs("\\n", out); ++ break; ++ case '\f': ++ fputs("\\f", out); ++ break; ++ case '\r': ++ fputs("\\r", out); ++ break; ++ default: ++ /* Other assorted control characters */ ++ fprintf(out, "\\u00%02x", c); ++ break; ++ } ++ } ++ fputc('"', out); ++} ++ ++ ++static inline void fputs_quoted_case(const char *data, FILE *out, int dir) + { + const char *p; + +@@ -34,16 +114,28 @@ static inline void fputs_quoted(const char *data, FILE *out) + for (p = data; p && *p; p++) { + if ((unsigned char) *p == 0x22 || /* " */ + (unsigned char) *p == 0x5c || /* \ */ ++ (unsigned char) *p == 0x60 || /* ` */ ++ (unsigned char) *p == 0x24 || /* $ */ + !isprint((unsigned char) *p) || + iscntrl((unsigned char) *p)) { + + fprintf(out, "\\x%02x", (unsigned char) *p); + } else +- fputc(*p, out); ++ fputc(dir == 1 ? toupper(*p) : ++ dir == -1 ? tolower(*p) : ++ *p, out); + } + fputc('"', out); + } + ++#define fputs_quoted(_d, _o) fputs_quoted_case(_d, _o, 0) ++#define fputs_quoted_upper(_d, _o) fputs_quoted_case(_d, _o, 1) ++#define fputs_quoted_lower(_d, _o) fputs_quoted_case(_d, _o, -1) ++ ++#define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0) ++#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1) ++#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1) ++ + static inline void fputs_nonblank(const char *data, FILE *out) + { + const char *p; +diff --git a/include/colors.h b/include/colors.h +index dd77bf6df..39c0edf46 100644 +--- a/include/colors.h ++++ b/include/colors.h +@@ -38,6 +38,11 @@ + + #define UL_COLOR_WHITE "\033[1;37m" + ++/* maximal length of human readable name of ESC seq. */ ++#define UL_COLORNAME_MAXSZ 32 ++ ++extern const char *color_sequence_from_colorname(const char *str); ++ + /* Initialize the global variable OUT_IS_TERM */ + extern int colors_init(void); + +diff --git a/include/mbsalign.h b/include/mbsalign.h +index 5eaf606e5..0c28e6f69 100644 +--- a/include/mbsalign.h ++++ b/include/mbsalign.h +@@ -46,11 +46,18 @@ 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 mbsalign_with_padding (const char *src, char *dest, size_t dest_size, ++ size_t *width, mbs_align_t align, int flags, ++ int padchar); ++ + 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 char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf, const char *safechars); + extern size_t mbs_safe_encode_size(size_t bytes); + ++extern char *mbs_invalid_encode(const char *s, size_t *width); ++extern char *mbs_invalid_encode_to_buffer(const char *s, size_t *width, char *buf); ++ + #endif /* UTIL_LINUX_MBSALIGN_H */ +diff --git a/include/strutils.h b/include/strutils.h +index 1f028e4ed..822fb7d49 100644 +--- a/include/strutils.h ++++ b/include/strutils.h +@@ -6,6 +6,7 @@ + #include <string.h> + #include <sys/types.h> + #include <stdio.h> ++#include <errno.h> + + /* default strtoxx_or_err() exit code */ + #ifndef STRTOXX_EXIT_CODE +@@ -57,20 +58,24 @@ static inline void xstrncpy(char *dest, const char *src, size_t n) + dest[n-1] = 0; + } + +-static inline char *strdup_to_offset(void *stru, size_t offset, const char *str) ++static inline int strdup_to_offset(void *stru, size_t offset, const char *str) + { + char *n = NULL; +- char **o = (char **) ((char *) stru + offset); ++ char **o; + ++ if (!stru) ++ return -EINVAL; ++ ++ o = (char **) ((char *) stru + offset); + if (str) { + n = strdup(str); + if (!n) +- return NULL; ++ return -ENOMEM; + } + + free(*o); + *o = n; +- return n; ++ return 0; + } + + #define strdup_to_struct_member(_s, _m, _str) \ +diff --git a/include/ttyutils.h b/include/ttyutils.h +index 13495ba96..47fe34472 100644 +--- a/include/ttyutils.h ++++ b/include/ttyutils.h +@@ -47,6 +47,7 @@ struct chardata { + (ptr)->capslock = 0; \ + } while (0) + ++extern int get_terminal_dimension(int *cols, int *lines); + extern int get_terminal_width(void); + extern int get_terminal_name(int fd, const char **path, const char **name, + const char **number); +diff --git a/lib/Makemodule.am b/lib/Makemodule.am +index acae27afb..714233c40 100644 +--- a/lib/Makemodule.am ++++ b/lib/Makemodule.am +@@ -6,6 +6,7 @@ libcommon_la_SOURCES = \ + lib/blkdev.c \ + lib/canonicalize.c \ + lib/colors.c \ ++ lib/color-names.c \ + lib/crc32.c \ + lib/env.c \ + lib/idcache.c \ +diff --git a/lib/color-names.c b/lib/color-names.c +new file mode 100644 +index 000000000..cf37670a9 +--- /dev/null ++++ b/lib/color-names.c +@@ -0,0 +1,57 @@ ++ ++#include "c.h" ++#include "colors.h" ++ ++struct ul_color_name { ++ const char *name; ++ const char *seq; ++}; ++ ++/* ++ * qsort/bsearch buddy ++ */ ++static int cmp_color_name(const void *a0, const void *b0) ++{ ++ struct ul_color_name *a = (struct ul_color_name *) a0, ++ *b = (struct ul_color_name *) b0; ++ return strcmp(a->name, b->name); ++} ++ ++/* ++ * Maintains human readable color names ++ */ ++const char *color_sequence_from_colorname(const char *str) ++{ ++ static const struct ul_color_name basic_schemes[] = { ++ { "black", UL_COLOR_BLACK }, ++ { "blink", UL_COLOR_BLINK }, ++ { "blue", UL_COLOR_BLUE }, ++ { "bold", UL_COLOR_BOLD }, ++ { "brown", UL_COLOR_BROWN }, ++ { "cyan", UL_COLOR_CYAN }, ++ { "darkgray", UL_COLOR_DARK_GRAY }, ++ { "gray", UL_COLOR_GRAY }, ++ { "green", UL_COLOR_GREEN }, ++ { "halfbright", UL_COLOR_HALFBRIGHT }, ++ { "lightblue", UL_COLOR_BOLD_BLUE }, ++ { "lightcyan", UL_COLOR_BOLD_CYAN }, ++ { "lightgray,", UL_COLOR_GRAY }, ++ { "lightgreen", UL_COLOR_BOLD_GREEN }, ++ { "lightmagenta", UL_COLOR_BOLD_MAGENTA }, ++ { "lightred", UL_COLOR_BOLD_RED }, ++ { "magenta", UL_COLOR_MAGENTA }, ++ { "red", UL_COLOR_RED }, ++ { "reset", UL_COLOR_RESET, }, ++ { "reverse", UL_COLOR_REVERSE }, ++ { "yellow", UL_COLOR_BOLD_YELLOW }, ++ }; ++ struct ul_color_name key = { .name = (char *) str }, *res; ++ ++ if (!str) ++ return NULL; ++ ++ res = bsearch(&key, basic_schemes, ARRAY_SIZE(basic_schemes), ++ sizeof(struct ul_color_name), ++ cmp_color_name); ++ return res ? res->seq : NULL; ++} +diff --git a/lib/mbsalign.c b/lib/mbsalign.c +index b307d19f7..8fdab9ee9 100644 +--- a/lib/mbsalign.c ++++ b/lib/mbsalign.c +@@ -27,9 +27,9 @@ + + #include "c.h" + #include "mbsalign.h" ++#include "strutils.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. */ +@@ -43,17 +43,19 @@ + */ + 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; + ++#ifdef HAVE_WIDECHAR ++ mbstate_t st; + memset(&st, 0, sizeof(st)); +- ++#endif + if (p && *p && bufsz) + last = p + (bufsz - 1); + + while (p && *p && p <= last) { +- if (iscntrl((unsigned char) *p)) { ++ if ((p < last && *p == '\\' && *(p + 1) == 'x') ++ || iscntrl((unsigned char) *p)) { + width += 4, bytes += 4; /* *p encoded to \x?? */ + p++; + } +@@ -106,28 +108,36 @@ size_t mbs_safe_width(const char *s) + + /* + * Copy @s to @buf and replace control and non-printable chars with +- * \x?? hex sequence. The @width returns number of cells. ++ * \x?? hex sequence. The @width returns number of cells. The @safechars ++ * are not encoded. + * + * 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) ++char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf, const char *safechars) + { +- mbstate_t st; + const char *p = s; + char *r; + size_t sz = s ? strlen(s) : 0; + ++#ifdef HAVE_WIDECHAR ++ mbstate_t st; ++ memset(&st, 0, sizeof(st)); ++#endif + if (!sz || !buf) + return NULL; + +- memset(&st, 0, sizeof(st)); +- + r = buf; + *width = 0; + + while (p && *p) { +- if (iscntrl((unsigned char) *p)) { ++ if (safechars && strchr(safechars, *p)) { ++ *r++ = *p++; ++ continue; ++ } ++ ++ if ((*p == '\\' && *(p + 1) == 'x') ++ || iscntrl((unsigned char) *p)) { + sprintf(r, "\\x%02x", (unsigned char) *p); + r += 4; + *width += 4; +@@ -152,13 +162,13 @@ char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf) + r += 4; + *width += 4; + } else { +- width++; ++ (*width)++; + *r++ = *p; + } + } else if (!iswprint(wc)) { + size_t i; + for (i = 0; i < len; i++) { +- sprintf(r, "\\x%02x", (unsigned char) *p); ++ sprintf(r, "\\x%02x", (unsigned char) p[i]); + r += 4; + *width += 4; + } +@@ -177,13 +187,76 @@ char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf) + *width += 4; + } else { + *r++ = *p++; +- *width++; ++ (*width)++; + } + #endif + } + + *r = '\0'; ++ return buf; ++} + ++/* ++ * Copy @s to @buf and replace broken sequences to \x?? hex sequence. The ++ * @width returns number of cells. The @safechars are not encoded. ++ * ++ * The @buf has to be big enough to store mbs_safe_encode_size(strlen(s))) ++ * bytes. ++ */ ++char *mbs_invalid_encode_to_buffer(const char *s, size_t *width, char *buf) ++{ ++ const char *p = s; ++ char *r; ++ size_t sz = s ? strlen(s) : 0; ++ ++#ifdef HAVE_WIDECHAR ++ mbstate_t st; ++ memset(&st, 0, sizeof(st)); ++#endif ++ if (!sz || !buf) ++ return NULL; ++ ++ r = buf; ++ *width = 0; ++ ++ while (p && *p) { ++#ifdef HAVE_WIDECHAR ++ wchar_t wc; ++ size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st); ++#else ++ size_t len = 1; ++#endif ++ ++ 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 (*p == '\\' && *(p + 1) == 'x') { ++ sprintf(r, "\\x%02x", (unsigned char) *p); ++ r += 4; ++ *width += 4; ++ } else { ++ memcpy(r, p, len); ++ r += len; ++ *width += wcwidth(wc); ++ } ++ p += len; ++ } ++ ++ *r = '\0'; + return buf; + } + +@@ -199,17 +272,39 @@ size_t mbs_safe_encode_size(size_t bytes) + char *mbs_safe_encode(const char *s, size_t *width) + { + size_t sz = s ? strlen(s) : 0; +- char *buf; ++ char *buf, *ret = NULL; + + if (!sz) + return NULL; + buf = malloc(mbs_safe_encode_size(sz)); +- if (!buf) +- return NULL; ++ if (buf) ++ ret = mbs_safe_encode_to_buffer(s, width, buf, NULL); ++ if (!ret) ++ free(buf); ++ return ret; ++} + +- return mbs_safe_encode_to_buffer(s, width, buf); ++/* ++ * Returns allocated string where all broken widechars chars are ++ * replaced with \x?? hex sequence. ++ */ ++char *mbs_invalid_encode(const char *s, size_t *width) ++{ ++ size_t sz = s ? strlen(s) : 0; ++ char *buf, *ret = NULL; ++ ++ if (!sz) ++ return NULL; ++ buf = malloc(mbs_safe_encode_size(sz)); ++ if (buf) ++ ret = mbs_invalid_encode_to_buffer(s, width, buf); ++ if (!ret) ++ free(buf); ++ return ret; + } + ++#ifdef HAVE_WIDECHAR ++ + static bool + wc_ensure_printable (wchar_t *wchars) + { +@@ -246,6 +341,7 @@ wc_truncate (wchar_t *wc, size_t width) + } + if (cells + next_cells > width) + break; ++ + cells += next_cells; + wc++; + } +@@ -273,7 +369,7 @@ rpl_wcswidth (const wchar_t *s, size_t n) + + return ret; + } +-#endif ++#endif /* HAVE_WIDECHAR */ + + /* Truncate multi-byte string to @width and returns number of + * bytes of the new string @str, and in @width returns number +@@ -290,7 +386,7 @@ mbs_truncate(char *str, size_t *width) + if (sz == (ssize_t) -1) + goto done; + +- wcs = malloc((sz + 1) * sizeof(wchar_t)); ++ wcs = calloc(1, (sz + 1) * sizeof(wchar_t)); + if (!wcs) + goto done; + +@@ -301,7 +397,7 @@ mbs_truncate(char *str, size_t *width) + done: + free(wcs); + #else +- if (*width < bytes) ++ if (bytes >= 0 && *width < (size_t) bytes) + bytes = *width; + #endif + if (bytes >= 0) +@@ -315,16 +411,23 @@ done: + A pointer to the terminating NUL is returned. */ + + static char* +-mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces) ++mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces, int padchar) + { + /* FIXME: Should we pad with "figure space" (\u2007) + if non ascii data present? */ +- while (n_spaces-- && (dest < dest_end)) +- *dest++ = ' '; ++ for (/* nothing */; n_spaces && (dest < dest_end); n_spaces--) ++ *dest++ = padchar; + *dest = '\0'; + return dest; + } + ++size_t ++mbsalign (const char *src, char *dest, size_t dest_size, ++ size_t *width, mbs_align_t align, int flags) ++{ ++ return mbsalign_with_padding(src, dest, dest_size, width, align, flags, ' '); ++} ++ + /* Align a string, SRC, in a field of *WIDTH columns, handling multi-byte + characters; write the result into the DEST_SIZE-byte buffer, DEST. + ALIGNMENT specifies whether to left- or right-justify or to center. +@@ -339,8 +442,14 @@ mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces) + Update *WIDTH to indicate how many columns were used before padding. */ + + size_t +-mbsalign (const char *src, char *dest, size_t dest_size, +- size_t *width, mbs_align_t align, int flags) ++mbsalign_with_padding (const char *src, char *dest, size_t dest_size, ++ size_t *width, mbs_align_t align, ++#ifdef HAVE_WIDECHAR ++ int flags, ++#else ++ int flags __attribute__((__unused__)), ++#endif ++ int padchar) + { + size_t ret = -1; + size_t src_size = strlen (src) + 1; +@@ -350,10 +459,11 @@ mbsalign (const char *src, char *dest, size_t dest_size, + size_t n_cols = src_size - 1; + size_t n_used_bytes = n_cols; /* Not including NUL */ + size_t n_spaces = 0, space_left; ++ ++#ifdef HAVE_WIDECHAR + bool conversion = false; + bool wc_enabled = false; + +-#ifdef HAVE_WIDECHAR + /* In multi-byte locales convert to wide characters + to allow easy truncation. Also determine number + of screen columns used. */ +@@ -407,9 +517,9 @@ mbsalign (const char *src, char *dest, size_t dest_size, + n_cols = wc_truncate (str_wc, *width); + n_used_bytes = wcstombs (newstr, str_wc, src_size); + } +-#endif + + mbsalign_unibyte: ++#endif + + if (n_cols > *width) /* Unibyte truncation required. */ + { +@@ -451,14 +561,14 @@ mbsalign_unibyte: + abort(); + } + +- dest = mbs_align_pad (dest, dest_end, start_spaces); ++ dest = mbs_align_pad (dest, dest_end, start_spaces, padchar); + space_left = dest_end - dest; + dest = mempcpy (dest, str_to_print, min (n_used_bytes, space_left)); +- mbs_align_pad (dest, dest_end, end_spaces); ++ mbs_align_pad (dest, dest_end, end_spaces, padchar); + } +- ++#ifdef HAVE_WIDECHAR + mbsalign_cleanup: +- ++#endif + free (str_wc); + free (newstr); + +diff --git a/lib/ttyutils.c b/lib/ttyutils.c +index ea551e26c..91497e763 100644 +--- a/lib/ttyutils.c ++++ b/lib/ttyutils.c +@@ -9,6 +9,58 @@ + #include "c.h" + #include "ttyutils.h" + ++/* ++ * Backported for RHEL7.6 libsmartcols ++ */ ++static int get_env_int(const char *name) ++{ ++ const char *cp = getenv(name); ++ ++ if (cp) { ++ char *end = NULL; ++ long x; ++ ++ errno = 0; ++ x = strtol(cp, &end, 10); ++ ++ if (errno == 0 && end && *end == '\0' && end > cp && ++ x > 0 && x <= INT_MAX) ++ return x; ++ } ++ ++ return -1; ++} ++ ++int get_terminal_dimension(int *cols, int *lines) ++{ ++ int c = 0, l = 0; ++ ++#if defined(TIOCGWINSZ) ++ struct winsize w_win; ++ if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &w_win) == 0) { ++ c = w_win.ws_col; ++ l = w_win.ws_row; ++ } ++#elif defined(TIOCGSIZE) ++ struct ttysize t_win; ++ if (ioctl (STDOUT_FILENO, TIOCGSIZE, &t_win) == 0) { ++ c = t_win.ts_cols; ++ l = t_win.ts_lines; ++ } ++#endif ++ ++ if (cols && c <= 0) ++ c = get_env_int("COLUMNS"); ++ if (lines && l <= 0) ++ l = get_env_int("LINES"); ++ ++ if (cols) ++ *cols = c; ++ if (lines) ++ *lines = l; ++ return 0; ++} ++ + int get_terminal_width(void) + { + #ifdef TIOCGSIZE +diff --git a/libfdisk/src/ask.c b/libfdisk/src/ask.c +index cdb4d0124..a10f3dc82 100644 +--- a/libfdisk/src/ask.c ++++ b/libfdisk/src/ask.c +@@ -42,7 +42,7 @@ const char *fdisk_ask_get_query(struct fdisk_ask *ask) + int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str) + { + assert(ask); +- return !strdup_to_struct_member(ask, query, str) ? -ENOMEM : 0; ++ return strdup_to_struct_member(ask, query, str); + } + + int fdisk_ask_get_type(struct fdisk_ask *ask) +@@ -90,7 +90,7 @@ const char *fdisk_ask_number_get_range(struct fdisk_ask *ask) + int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range) + { + assert(ask); +- return !strdup_to_struct_member(ask, data.num.range, range) ? -ENOMEM : 0; ++ return strdup_to_struct_member(ask, data.num.range, range); + } + + uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask) +-- +2.14.4 + diff --git a/SOURCES/0153-libsmartcols-backport-upstream-version-v2.32-158-gc0.patch b/SOURCES/0153-libsmartcols-backport-upstream-version-v2.32-158-gc0.patch new file mode 100644 index 0000000..ab8ef8d --- /dev/null +++ b/SOURCES/0153-libsmartcols-backport-upstream-version-v2.32-158-gc0.patch @@ -0,0 +1,6320 @@ +From fc6853caa0f3e4f8f10404e58f2dbf9f0df88bd4 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 31 May 2018 11:44:35 +0200 +Subject: [PATCH 153/173] libsmartcols: backport upstream version + v2.32-158-gc0bdff999 + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1561350 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + Makefile.am | 1 + + configure.ac | 3 - + libsmartcols/Makemodule.am | 3 +- + libsmartcols/docs/Makefile.am | 4 +- + libsmartcols/docs/libsmartcols-docs.xml | 28 +- + libsmartcols/docs/libsmartcols-sections.txt | 47 +- + libsmartcols/samples/Makemodule.am | 37 + + libsmartcols/samples/continuous.c | 138 +++ + libsmartcols/samples/fromfile.c | 344 +++++++ + libsmartcols/samples/maxout.c | 56 ++ + libsmartcols/samples/title.c | 135 +++ + libsmartcols/{src/test.c => samples/tree.c} | 77 +- + libsmartcols/samples/wrap.c | 111 +++ + libsmartcols/src/Makemodule.am | 41 +- + libsmartcols/src/cell.c | 111 ++- + libsmartcols/src/column.c | 351 +++++-- + libsmartcols/src/iter.c | 2 +- + libsmartcols/src/libsmartcols.h.in | 156 +++- + libsmartcols/src/libsmartcols.sym | 72 ++ + libsmartcols/src/line.c | 173 ++-- + libsmartcols/src/smartcolsP.h | 70 +- + libsmartcols/src/symbols.c | 114 ++- + libsmartcols/src/table.c | 851 +++++++++++++---- + libsmartcols/src/table_print.c | 1342 ++++++++++++++++++++++----- + 24 files changed, 3530 insertions(+), 737 deletions(-) + create mode 100644 libsmartcols/samples/Makemodule.am + create mode 100644 libsmartcols/samples/continuous.c + create mode 100644 libsmartcols/samples/fromfile.c + create mode 100644 libsmartcols/samples/maxout.c + create mode 100644 libsmartcols/samples/title.c + rename libsmartcols/{src/test.c => samples/tree.c} (68%) + create mode 100644 libsmartcols/samples/wrap.c + +diff --git a/Makefile.am b/Makefile.am +index 67464e4b2..7d5fa10e9 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -118,6 +118,7 @@ edit_cmd = sed \ + -e 's|@VERSION[@]|$(VERSION)|g' \ + -e 's|@LIBUUID_VERSION[@]|$(LIBUUID_VERSION)|g' \ + -e 's|@LIBMOUNT_VERSION[@]|$(LIBMOUNT_VERSION)|g' \ ++ -e 's|@LIBSMARTCOLS_VERSION[@]|$(LIBSMARTCOLS_VERSION)|g' \ + -e 's|@LIBBLKID_VERSION[@]|$(LIBBLKID_VERSION)|g' + + CLEANFILES += $(PATHFILES) +diff --git a/configure.ac b/configure.ac +index 8cf317dc0..d561e01d0 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -133,9 +133,6 @@ AC_SUBST([BSD_WARN_CFLAGS]) + dnl libtool-2 + LT_INIT + +-dnl check supported linker flags +-AX_CHECK_VSCRIPT +- + m4_ifndef([PKG_PROG_PKG_CONFIG], + [m4_fatal([Could not locate the pkg-config autoconf + macros. These are usually located in /usr/share/aclocal/pkg.m4. +diff --git a/libsmartcols/Makemodule.am b/libsmartcols/Makemodule.am +index 0089712f1..012848b2b 100644 +--- a/libsmartcols/Makemodule.am ++++ b/libsmartcols/Makemodule.am +@@ -1,13 +1,14 @@ + if BUILD_LIBSMARTCOLS + + include libsmartcols/src/Makemodule.am ++include libsmartcols/samples/Makemodule.am + + if ENABLE_GTK_DOC + # Docs uses separate Makefiles + SUBDIRS += libsmartcols/docs + endif + +-# noinst for RHEL7: pkgconfig_DATA += libsmartcols/smartcols.pc ++pkgconfig_DATA += libsmartcols/smartcols.pc + PATHFILES += libsmartcols/smartcols.pc + EXTRA_DIST += libsmartcols/COPYING + +diff --git a/libsmartcols/docs/Makefile.am b/libsmartcols/docs/Makefile.am +index c5aa2237c..e8a7600e9 100644 +--- a/libsmartcols/docs/Makefile.am ++++ b/libsmartcols/docs/Makefile.am +@@ -32,7 +32,7 @@ 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 ++MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space scols + + # Extra options to supply to gtkdoc-mktmpl + # e.g. MKTMPL_OPTIONS=--only-section-tmpl +@@ -67,7 +67,7 @@ HTML_IMAGES= + # 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 ++# SGML files where gtk-doc abbreviations (#GtkWidget) are expanded + # These files must be listed here *and* in content_files + # e.g. expand_content_files=running.sgml + expand_content_files= +diff --git a/libsmartcols/docs/libsmartcols-docs.xml b/libsmartcols/docs/libsmartcols-docs.xml +index 4976ba701..02ee1ffe1 100644 +--- a/libsmartcols/docs/libsmartcols-docs.xml ++++ b/libsmartcols/docs/libsmartcols-docs.xml +@@ -9,12 +9,12 @@ + <title>libsmartcols Reference Manual</title> + <releaseinfo>for libsmartcols version &version;</releaseinfo> + <copyright> +- <year>2014</year> ++ <year>2014-2018</year> + <holder>Karel Zak <kzak@redhat.com></holder> + </copyright> + </bookinfo> + +- <part id="gtk"> ++ <part id="overview"> + <title>libsmartcols Overview</title> + <partintro> + <para> +@@ -22,7 +22,7 @@ The libsmartcols library is used for smart adaptive formatting of tabular data. + </para> + <para> + 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/. ++available from https://www.kernel.org/pub/linux/utils/util-linux/. + </para> + </partintro> + </part> +@@ -45,8 +45,28 @@ available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/. + <xi:include href="xml/version-utils.xml"/> + <xi:include href="xml/init.xml"/> + </part> +- <index id="api-index-full"> ++ <index id="api-index"> + <title>API Index</title> + <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include> + </index> ++ <index role="2.27"> ++ <title>Index of new symbols in 2.27</title> ++ <xi:include href="xml/api-index-2.27.xml"><xi:fallback /></xi:include> ++ </index> ++ <index role="2.28"> ++ <title>Index of new symbols in 2.28</title> ++ <xi:include href="xml/api-index-2.28.xml"><xi:fallback /></xi:include> ++ </index> ++ <index role="2.29"> ++ <title>Index of new symbols in 2.29</title> ++ <xi:include href="xml/api-index-2.29.xml"><xi:fallback /></xi:include> ++ </index> ++ <index role="2.30"> ++ <title>Index of new symbols in 2.30</title> ++ <xi:include href="xml/api-index-2.30.xml"><xi:fallback /></xi:include> ++ </index> ++ <index role="2.31"> ++ <title>Index of new symbols in 2.31</title> ++ <xi:include href="xml/api-index-2.31.xml"><xi:fallback /></xi:include> ++ </index> + </book> +diff --git a/libsmartcols/docs/libsmartcols-sections.txt b/libsmartcols/docs/libsmartcols-sections.txt +index 2b8180c52..79786b544 100644 +--- a/libsmartcols/docs/libsmartcols-sections.txt ++++ b/libsmartcols/docs/libsmartcols-sections.txt +@@ -2,12 +2,15 @@ + <FILE>cell</FILE> + libscols_cell + scols_cell_copy_content ++scols_cell_get_alignment + scols_cell_get_color + scols_cell_get_data ++scols_cell_get_flags + scols_cell_get_userdata + scols_cell_refer_data + scols_cell_set_color + scols_cell_set_data ++scols_cell_set_flags + scols_cell_set_userdata + scols_cmpstr_cells + scols_reset_cell +@@ -19,20 +22,32 @@ libscols_column + scols_column_get_color + scols_column_get_flags + scols_column_get_header ++scols_column_get_json_type ++scols_column_get_safechars ++scols_column_get_table + scols_column_get_whint ++scols_column_get_width ++scols_column_is_customwrap ++scols_column_is_hidden + scols_column_is_noextremes + scols_column_is_right + scols_column_is_strict_width + scols_column_is_tree + scols_column_is_trunc ++scols_column_is_wrap + scols_column_set_cmpfunc + scols_column_set_color + scols_column_set_flags ++scols_column_set_json_type ++scols_column_set_safechars + scols_column_set_whint ++scols_column_set_wrapfunc + scols_copy_column + scols_new_column + scols_ref_column + scols_unref_column ++scols_wrapnl_chunksize ++scols_wrapnl_nextchunk + </SECTION> + + <SECTION> +@@ -58,10 +73,13 @@ scols_line_get_ncells + scols_line_get_parent + scols_line_get_userdata + scols_line_has_children ++scols_line_is_ancestor + scols_line_next_child ++scols_line_refer_column_data + scols_line_refer_data + scols_line_remove_child + scols_line_set_color ++scols_line_set_column_data + scols_line_set_data + scols_line_set_userdata + scols_new_line +@@ -78,6 +96,8 @@ scols_ref_symbols + scols_symbols_set_branch + scols_symbols_set_right + scols_symbols_set_vertical ++scols_symbols_set_title_padding ++scols_symbols_set_cell_padding + scols_unref_symbols + </SECTION> + +@@ -87,29 +107,48 @@ libscols_table + scols_copy_table + scols_new_table + scols_ref_table ++scols_sort_table ++scols_sort_table_by_tree + scols_table_add_column + scols_table_add_line + scols_table_colors_wanted + scols_table_enable_ascii + scols_table_enable_colors ++scols_table_enable_noencoding + scols_table_enable_export ++scols_table_enable_header_repeat ++scols_table_enable_json + scols_table_enable_maxout + scols_table_enable_noheadings ++scols_table_enable_nolinesep ++scols_table_enable_nowrap + 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_name + scols_table_get_ncols + scols_table_get_nlines + scols_table_get_stream ++scols_table_get_symbols ++scols_table_get_termforce ++scols_table_get_termheight ++scols_table_get_termwidth ++scols_table_get_title + scols_table_is_ascii + scols_table_is_empty + scols_table_is_export ++scols_table_is_header_repeat ++scols_table_is_json + scols_table_is_maxout + scols_table_is_noheadings ++scols_table_is_noencoding ++scols_table_is_nolinesep ++scols_table_is_nowrap + scols_table_is_raw + scols_table_is_tree ++scols_table_move_column + scols_table_new_column + scols_table_new_line + scols_table_next_column +@@ -120,10 +159,14 @@ scols_table_remove_columns + scols_table_remove_line + scols_table_remove_lines + scols_table_set_column_separator ++scols_table_set_default_symbols + scols_table_set_line_separator ++scols_table_set_name + scols_table_set_stream + scols_table_set_symbols +-scols_sort_table ++scols_table_set_termforce ++scols_table_set_termheight ++scols_table_set_termwidth + scols_unref_table + </SECTION> + +@@ -131,6 +174,8 @@ scols_unref_table + <FILE>table_print</FILE> + scols_print_table + scols_print_table_to_string ++scols_table_print_range ++scols_table_print_range_to_string + </SECTION> + + <SECTION> +diff --git a/libsmartcols/samples/Makemodule.am b/libsmartcols/samples/Makemodule.am +new file mode 100644 +index 000000000..0e0208f04 +--- /dev/null ++++ b/libsmartcols/samples/Makemodule.am +@@ -0,0 +1,37 @@ ++ ++check_PROGRAMS += \ ++ sample-scols-title \ ++ sample-scols-wrap \ ++ sample-scols-continuous \ ++ sample-scols-fromfile \ ++ sample-scols-maxout ++ ++sample_scols_cflags = $(AM_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) \ ++ -I$(ul_libsmartcols_incdir) ++sample_scols_ldadd = libsmartcols.la $(LDADD) ++ ++check_PROGRAMS += sample-scols-tree ++sample_scols_tree_SOURCES = libsmartcols/samples/tree.c ++sample_scols_tree_LDADD = $(sample_scols_ldadd) libcommon.la ++sample_scols_tree_CFLAGS = $(sample_scols_cflags) ++ ++sample_scols_title_SOURCES = libsmartcols/samples/title.c ++sample_scols_title_LDADD = $(sample_scols_ldadd) libcommon.la ++sample_scols_title_CFLAGS = $(sample_scols_cflags) ++ ++sample_scols_wrap_SOURCES = libsmartcols/samples/wrap.c ++sample_scols_wrap_LDADD = $(sample_scols_ldadd) ++sample_scols_wrap_CFLAGS = $(sample_scols_cflags) ++ ++sample_scols_continuous_SOURCES = libsmartcols/samples/continuous.c ++sample_scols_continuous_LDADD = $(sample_scols_ldadd) libcommon.la ++sample_scols_continuous_CFLAGS = $(sample_scols_cflags) ++ ++sample_scols_maxout_SOURCES = libsmartcols/samples/maxout.c ++sample_scols_maxout_LDADD = $(sample_scols_ldadd) ++sample_scols_maxout_CFLAGS = $(sample_scols_cflags) ++ ++sample_scols_fromfile_SOURCES = libsmartcols/samples/fromfile.c ++sample_scols_fromfile_LDADD = $(sample_scols_ldadd) libcommon.la ++sample_scols_fromfile_CFLAGS = $(sample_scols_cflags) ++ +diff --git a/libsmartcols/samples/continuous.c b/libsmartcols/samples/continuous.c +new file mode 100644 +index 000000000..8f9d13e6b +--- /dev/null ++++ b/libsmartcols/samples/continuous.c +@@ -0,0 +1,138 @@ ++/* ++ * Copyright (C) 2016 Karel Zak <kzak@redhat.com> ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++#include <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <errno.h> ++#include <sys/types.h> ++#include <sys/time.h> ++ ++#include "c.h" ++#include "nls.h" ++#include "strutils.h" ++#include "xalloc.h" ++ ++#include "libsmartcols.h" ++ ++#define TIME_PERIOD 3.0 /* seconds */ ++ ++enum { COL_NUM, COL_DATA, COL_TIME }; ++ ++static double time_diff(struct timeval *a, struct timeval *b) ++{ ++ return (a->tv_sec - b->tv_sec) + (a->tv_usec - b->tv_usec) / 1E6; ++} ++ ++/* add columns to the @tb */ ++static void setup_columns(struct libscols_table *tb) ++{ ++ scols_table_enable_maxout(tb, 1); ++ if (!scols_table_new_column(tb, "#NUM", 0.1, SCOLS_FL_RIGHT)) ++ goto fail; ++ if (!scols_table_new_column(tb, "DATA", 0.7, 0)) ++ goto fail; ++ if (!scols_table_new_column(tb, "TIME", 0.2, 0)) ++ goto fail; ++ return; ++fail: ++ scols_unref_table(tb); ++ err(EXIT_FAILURE, "failed to create output columns"); ++} ++ ++static struct libscols_line *add_line(struct libscols_table *tb, size_t i) ++{ ++ char *p; ++ struct libscols_line *ln = scols_table_new_line(tb, NULL); ++ ++ if (!ln) ++ err(EXIT_FAILURE, "failed to create output line"); ++ ++ xasprintf(&p, "%zu", i); ++ if (scols_line_refer_data(ln, COL_NUM, p)) ++ goto fail; ++ ++ xasprintf(&p, "data-%02zu-%02zu-%02zu-end", i + 1, i + 2, i + 3); ++ if (scols_line_refer_data(ln, COL_DATA, p)) ++ goto fail; ++ ++ return ln; ++fail: ++ scols_unref_table(tb); ++ err(EXIT_FAILURE, "failed to create output line"); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ struct libscols_table *tb; ++ size_t i; ++ struct timeval last; ++ ++ scols_init_debug(0); ++ ++ tb = scols_new_table(); ++ if (!tb) ++ err(EXIT_FAILURE, "failed to create output table"); ++ ++ setup_columns(tb); ++ gettimeofday(&last, NULL); ++ ++ for (i = 0; i < 10; i++) { ++ struct libscols_line *line; ++ struct timeval now; ++ int done = 0; ++ char *timecell = xmalloc( sizeof(stringify_value(UINT_MAX)) ); ++ ++ line = add_line(tb, i); ++ ++ /* Make a reference from cell data to the buffer, then we can ++ * update cell data without any interaction with libsmartcols ++ */ ++ scols_line_refer_data(line, COL_TIME, timecell); ++ ++ do { ++ double diff; ++ ++ gettimeofday(&now, NULL); ++ diff = time_diff(&now, &last); ++ ++ if (now.tv_sec == last.tv_sec + (long) TIME_PERIOD) ++ done = 1; ++ else ++ usleep(100000); ++ ++ /* update "TIME" cell data */ ++ sprintf(timecell, "%f [%3d%%]", diff, ++ done ? 100 : (int)(diff / (TIME_PERIOD / 100.0))); ++ ++ /* Note that libsmartcols don't print \n for last line ++ * in the table, but if you print a line somewhere in ++ * the midle of the table you need ++ * ++ * scols_table_enable_nolinesep(tb, !done); ++ * ++ * to disable line breaks. In this example it's ++ * unnecessary as we print the latest line only. ++ */ ++ ++ /* print the line */ ++ scols_table_print_range(tb, line, NULL); ++ ++ if (!done) { ++ /* terminal is waiting for \n, fflush() to force output */ ++ fflush(scols_table_get_stream(tb)); ++ /* move to the begin of the line */ ++ fputc('\r', scols_table_get_stream(tb)); ++ } else ++ fputc('\n', scols_table_get_stream(tb)); ++ } while (!done); ++ ++ last = now; ++ } ++ ++ scols_unref_table(tb); ++ return EXIT_SUCCESS; ++} +diff --git a/libsmartcols/samples/fromfile.c b/libsmartcols/samples/fromfile.c +new file mode 100644 +index 000000000..c1ab728fd +--- /dev/null ++++ b/libsmartcols/samples/fromfile.c +@@ -0,0 +1,344 @@ ++/* ++ * Copyright (C) 2016 Karel Zak <kzak@redhat.com> ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++#include <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <errno.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <dirent.h> ++#include <getopt.h> ++ ++#include "c.h" ++#include "nls.h" ++#include "strutils.h" ++#include "xalloc.h" ++#include "optutils.h" ++ ++#include "libsmartcols.h" ++ ++struct column_flag { ++ const char *name; ++ int mask; ++}; ++ ++static const struct column_flag flags[] = { ++ { "trunc", SCOLS_FL_TRUNC }, ++ { "tree", SCOLS_FL_TREE }, ++ { "right", SCOLS_FL_RIGHT }, ++ { "strictwidth",SCOLS_FL_STRICTWIDTH }, ++ { "noextremes", SCOLS_FL_NOEXTREMES }, ++ { "hidden", SCOLS_FL_HIDDEN }, ++ { "wrap", SCOLS_FL_WRAP }, ++ { "wrapnl", SCOLS_FL_WRAP }, ++ { "none", 0 } ++}; ++ ++static long name_to_flag(const char *name, size_t namesz) ++{ ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(flags); i++) { ++ const char *cn = flags[i].name; ++ ++ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) ++ return flags[i].mask; ++ } ++ warnx("unknown flag: %s", name); ++ return -1; ++} ++ ++static int parse_column_flags(char *str) ++{ ++ unsigned long num_flags = 0; ++ ++ if (string_to_bitmask(str, &num_flags, name_to_flag)) ++ err(EXIT_FAILURE, "failed to parse column flags"); ++ ++ return num_flags; ++} ++ ++static struct libscols_column *parse_column(FILE *f) ++{ ++ size_t len = 0; ++ char *line = NULL; ++ int nlines = 0; ++ ++ struct libscols_column *cl = NULL; ++ ++ while (getline(&line, &len, f) != -1) { ++ ++ char *p = strrchr(line, '\n'); ++ if (p) ++ *p = '\0'; ++ ++ switch (nlines) { ++ case 0: /* NAME */ ++ { ++ struct libscols_cell *hr; ++ ++ cl = scols_new_column(); ++ if (!cl) ++ goto fail; ++ hr = scols_column_get_header(cl); ++ if (!hr || scols_cell_set_data(hr, line)) ++ goto fail; ++ break; ++ } ++ case 1: /* WIDTH-HINT */ ++ { ++ double whint = strtod_or_err(line, "failed to parse column whint"); ++ if (scols_column_set_whint(cl, whint)) ++ goto fail; ++ break; ++ } ++ case 2: /* FLAGS */ ++ { ++ int num_flags = parse_column_flags(line); ++ if (scols_column_set_flags(cl, num_flags)) ++ goto fail; ++ if (strcmp(line, "wrapnl") == 0) { ++ scols_column_set_wrapfunc(cl, ++ scols_wrapnl_chunksize, ++ scols_wrapnl_nextchunk, ++ NULL); ++ scols_column_set_safechars(cl, "\n"); ++ } ++ break; ++ } ++ case 3: /* COLOR */ ++ if (scols_column_set_color(cl, line)) ++ goto fail; ++ break; ++ default: ++ break; ++ } ++ ++ nlines++; ++ } ++ ++ free(line); ++ return cl; ++fail: ++ free(line); ++ scols_unref_column(cl); ++ return NULL; ++} ++ ++static int parse_column_data(FILE *f, struct libscols_table *tb, int col) ++{ ++ size_t len = 0, nlines = 0; ++ int i; ++ char *str = NULL; ++ ++ while ((i = getline(&str, &len, f)) != -1) { ++ ++ struct libscols_line *ln; ++ char *p = strrchr(str, '\n'); ++ if (p) ++ *p = '\0'; ++ ++ while ((p = strrchr(str, '\\')) && *(p + 1) == 'n') { ++ *p = '\n'; ++ memmove(p + 1, p + 2, i - (p + 2 - str)); ++ } ++ ++ ln = scols_table_get_line(tb, nlines++); ++ if (!ln) ++ break; ++ ++ scols_line_set_data(ln, col, str); ++ } ++ ++ free(str); ++ return 0; ++ ++} ++ ++static struct libscols_line *get_line_with_id(struct libscols_table *tb, ++ int col_id, const char *id) ++{ ++ struct libscols_line *ln; ++ struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); ++ ++ while (scols_table_next_line(tb, itr, &ln) == 0) { ++ struct libscols_cell *ce = scols_line_get_cell(ln, col_id); ++ const char *data = ce ? scols_cell_get_data(ce) : NULL; ++ ++ if (data && strcmp(data, id) == 0) ++ break; ++ } ++ ++ scols_free_iter(itr); ++ return ln; ++} ++ ++static void compose_tree(struct libscols_table *tb, int parent_col, int id_col) ++{ ++ struct libscols_line *ln; ++ struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); ++ ++ while (scols_table_next_line(tb, itr, &ln) == 0) { ++ struct libscols_line *parent = NULL; ++ struct libscols_cell *ce = scols_line_get_cell(ln, parent_col); ++ const char *data = ce ? scols_cell_get_data(ce) : NULL; ++ ++ if (data) ++ parent = get_line_with_id(tb, id_col, data); ++ if (parent) ++ scols_line_add_child(parent, ln); ++ } ++ ++ scols_free_iter(itr); ++} ++ ++ ++static void __attribute__((__noreturn__)) usage(void) ++{ ++ FILE *out = stdout; ++ fprintf(out, ++ "\n %s [options] <column-data-file> ...\n\n", program_invocation_short_name); ++ ++ fputs(" -m, --maxout fill all terminal width\n", out); ++ fputs(" -c, --column <file> column definition\n", out); ++ fputs(" -n, --nlines <num> number of lines\n", out); ++ fputs(" -J, --json JSON output format\n", out); ++ fputs(" -r, --raw RAW output format\n", out); ++ fputs(" -E, --export use key=\"value\" output format\n", out); ++ fputs(" -C, --colsep <str> set columns separator\n", out); ++ fputs(" -w, --width <num> hardcode terminal width\n", out); ++ fputs(" -p, --tree-parent-column <n> parent column\n", out); ++ fputs(" -i, --tree-id-column <n> id column\n", out); ++ fputs(" -h, --help this help\n", out); ++ fputs("\n", out); ++ ++ exit(EXIT_SUCCESS); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ struct libscols_table *tb; ++ int c, n, nlines = 0; ++ int parent_col = -1, id_col = -1; ++ ++ static const struct option longopts[] = { ++ { "maxout", 0, NULL, 'm' }, ++ { "column", 1, NULL, 'c' }, ++ { "nlines", 1, NULL, 'n' }, ++ { "width", 1, NULL, 'w' }, ++ { "tree-parent-column", 1, NULL, 'p' }, ++ { "tree-id-column", 1, NULL, 'i' }, ++ { "json", 0, NULL, 'J' }, ++ { "raw", 0, NULL, 'r' }, ++ { "export", 0, NULL, 'E' }, ++ { "colsep", 1, NULL, 'C' }, ++ { "help", 0, NULL, 'h' }, ++ { NULL, 0, NULL, 0 }, ++ }; ++ ++ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ ++ { 'E', 'J', 'r' }, ++ { 0 } ++ }; ++ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; ++ ++ setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */ ++ scols_init_debug(0); ++ ++ tb = scols_new_table(); ++ if (!tb) ++ err(EXIT_FAILURE, "failed to create output table"); ++ ++ while((c = getopt_long(argc, argv, "hCc:Ei:Jmn:p:rw:", longopts, NULL)) != -1) { ++ ++ err_exclusive_options(c, longopts, excl, excl_st); ++ ++ switch(c) { ++ case 'c': /* add column from file */ ++ { ++ struct libscols_column *cl; ++ FILE *f = fopen(optarg, "r"); ++ ++ if (!f) ++ err(EXIT_FAILURE, "%s: open failed", optarg); ++ cl = parse_column(f); ++ if (cl && scols_table_add_column(tb, cl)) ++ err(EXIT_FAILURE, "%s: failed to add column", optarg); ++ scols_unref_column(cl); ++ fclose(f); ++ break; ++ } ++ case 'p': ++ parent_col = strtou32_or_err(optarg, "failed to parse tree PARENT column"); ++ break; ++ case 'i': ++ id_col = strtou32_or_err(optarg, "failed to parse tree ID column"); ++ break; ++ case 'J': ++ scols_table_enable_json(tb, 1); ++ scols_table_set_name(tb, "testtable"); ++ break; ++ case 'm': ++ scols_table_enable_maxout(tb, TRUE); ++ break; ++ case 'r': ++ scols_table_enable_raw(tb, TRUE); ++ break; ++ case 'E': ++ scols_table_enable_export(tb, TRUE); ++ break; ++ case 'C': ++ scols_table_set_column_separator(tb, optarg); ++ break; ++ case 'n': ++ nlines = strtou32_or_err(optarg, "failed to parse number of lines"); ++ break; ++ case 'w': ++ scols_table_set_termforce(tb, SCOLS_TERMFORCE_ALWAYS); ++ scols_table_set_termwidth(tb, strtou32_or_err(optarg, "failed to parse terminal width")); ++ break; ++ case 'h': ++ usage(); ++ default: ++ errtryhelp(EXIT_FAILURE); ++ } ++ } ++ ++ if (nlines <= 0) ++ errx(EXIT_FAILURE, "--nlines not set"); ++ ++ for (n = 0; n < nlines; n++) { ++ struct libscols_line *ln = scols_new_line(); ++ ++ if (!ln || scols_table_add_line(tb, ln)) ++ err(EXIT_FAILURE, "failed to add a new line"); ++ ++ scols_unref_line(ln); ++ } ++ ++ n = 0; ++ ++ while (optind < argc) { ++ FILE *f = fopen(argv[optind], "r"); ++ ++ if (!f) ++ err(EXIT_FAILURE, "%s: open failed", argv[optind]); ++ ++ parse_column_data(f, tb, n); ++ optind++; ++ n++; ++ } ++ ++ if (scols_table_is_tree(tb) && parent_col >= 0 && id_col >= 0) ++ compose_tree(tb, parent_col, id_col); ++ ++ scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); ++ ++ scols_print_table(tb); ++ scols_unref_table(tb); ++ return EXIT_SUCCESS; ++} +diff --git a/libsmartcols/samples/maxout.c b/libsmartcols/samples/maxout.c +new file mode 100644 +index 000000000..07a05e13f +--- /dev/null ++++ b/libsmartcols/samples/maxout.c +@@ -0,0 +1,56 @@ ++/* ++ * Copyright (C) 2016 Karel Zak <kzak@redhat.com> ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++#include <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <errno.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <dirent.h> ++#include <getopt.h> ++ ++#include "c.h" ++#include "nls.h" ++#include "libsmartcols.h" ++ ++enum { COL_LEFT, COL_FOO, COL_RIGHT }; ++ ++int main(int argc, char *argv[]) ++{ ++ struct libscols_table *tb; ++ int rc = -1, nlines = 3; ++ ++ setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */ ++ ++ scols_init_debug(0); ++ ++ tb = scols_new_table(); ++ if (!tb) ++ err(EXIT_FAILURE, "failed to create output table"); ++ ++ scols_table_enable_maxout(tb, TRUE); ++ if (!scols_table_new_column(tb, "LEFT", 0, 0)) ++ goto done; ++ if (!scols_table_new_column(tb, "FOO", 0, 0)) ++ goto done; ++ if (!scols_table_new_column(tb, "RIGHT", 0, SCOLS_FL_RIGHT)) ++ goto done; ++ ++ while (nlines--) { ++ struct libscols_line *ln = scols_table_new_line(tb, NULL); ++ ++ scols_line_set_data(ln, COL_LEFT, "A"); ++ scols_line_set_data(ln, COL_FOO, "B"); ++ scols_line_set_data(ln, COL_RIGHT, "C"); ++ } ++ ++ scols_print_table(tb); ++ rc = 0; ++done: ++ scols_unref_table(tb); ++ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; ++} +diff --git a/libsmartcols/samples/title.c b/libsmartcols/samples/title.c +new file mode 100644 +index 000000000..131400da4 +--- /dev/null ++++ b/libsmartcols/samples/title.c +@@ -0,0 +1,135 @@ ++/* ++ * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com> ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++#include <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <errno.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <dirent.h> ++#include <getopt.h> ++ ++#include "c.h" ++#include "nls.h" ++#include "strutils.h" ++#include "xalloc.h" ++ ++#include "libsmartcols.h" ++ ++ ++enum { COL_NAME, COL_DATA }; ++ ++/* add columns to the @tb */ ++static void setup_columns(struct libscols_table *tb) ++{ ++ if (!scols_table_new_column(tb, "NAME", 0, 0)) ++ goto fail; ++ if (!scols_table_new_column(tb, "DATA", 0, 0)) ++ goto fail; ++ return; ++fail: ++ scols_unref_table(tb); ++ err(EXIT_FAILURE, "failed to create output columns"); ++} ++ ++static void add_line(struct libscols_table *tb, const char *name, const char *data) ++{ ++ struct libscols_line *ln = scols_table_new_line(tb, NULL); ++ if (!ln) ++ err(EXIT_FAILURE, "failed to create output line"); ++ ++ if (scols_line_set_data(ln, COL_NAME, name)) ++ goto fail; ++ if (scols_line_set_data(ln, COL_DATA, data)) ++ goto fail; ++ return; ++fail: ++ scols_unref_table(tb); ++ err(EXIT_FAILURE, "failed to create output line"); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ struct libscols_table *tb; ++ struct libscols_symbols *sy; ++ struct libscols_cell *title; ++ int c; ++ ++ static const struct option longopts[] = { ++ { "maxout", 0, NULL, 'm' }, ++ { "width", 1, NULL, 'w' }, ++ { "help", 1, NULL, 'h' }, ++ ++ { NULL, 0, NULL, 0 }, ++ }; ++ ++ setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */ ++ ++ scols_init_debug(0); ++ ++ tb = scols_new_table(); ++ if (!tb) ++ err(EXIT_FAILURE, "failed to create output table"); ++ ++ while((c = getopt_long(argc, argv, "hmw:", longopts, NULL)) != -1) { ++ switch(c) { ++ case 'h': ++ printf("%s [--help | --maxout | --width <num>]\n", program_invocation_short_name); ++ break; ++ case 'm': ++ scols_table_enable_maxout(tb, TRUE); ++ break; ++ case 'w': ++ scols_table_set_termforce(tb, SCOLS_TERMFORCE_ALWAYS); ++ scols_table_set_termwidth(tb, strtou32_or_err(optarg, "failed to parse terminal width")); ++ break; ++ } ++ } ++ ++ scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); ++ setup_columns(tb); ++ add_line(tb, "foo", "bla bla bla"); ++ add_line(tb, "bar", "alb alb alb"); ++ ++ title = scols_table_get_title(tb); ++ ++ /* right */ ++ scols_cell_set_data(title, "This is right title"); ++ scols_cell_set_color(title, "red"); ++ scols_cell_set_flags(title, SCOLS_CELL_FL_RIGHT); ++ scols_print_table(tb); ++ ++ /* left without padding */ ++ scols_cell_set_data(title, "This is left title (without padding)"); ++ scols_cell_set_color(title, "yellow"); ++ scols_cell_set_flags(title, SCOLS_CELL_FL_LEFT); ++ scols_print_table(tb); ++ ++ /* center */ ++ sy = scols_new_symbols(); ++ if (!sy) ++ err_oom(); ++ scols_table_set_symbols(tb, sy); ++ scols_unref_symbols(sy); ++ ++ scols_symbols_set_title_padding(sy, "="); ++ scols_cell_set_data(title, "This is center title (with padding)"); ++ scols_cell_set_color(title, "green"); ++ scols_cell_set_flags(title, SCOLS_CELL_FL_CENTER); ++ scols_print_table(tb); ++ ++ /* left with padding */ ++ scols_symbols_set_title_padding(sy, "-"); ++ scols_cell_set_data(title, "This is left title (with padding)"); ++ scols_cell_set_color(title, "blue"); ++ scols_cell_set_flags(title, SCOLS_CELL_FL_LEFT); ++ scols_print_table(tb); ++ ++ ++ scols_unref_table(tb); ++ return EXIT_SUCCESS; ++} +diff --git a/libsmartcols/src/test.c b/libsmartcols/samples/tree.c +similarity index 68% +rename from libsmartcols/src/test.c +rename to libsmartcols/samples/tree.c +index dd87fd38b..0cdb99420 100644 +--- a/libsmartcols/src/test.c ++++ b/libsmartcols/samples/tree.c +@@ -39,7 +39,7 @@ static void setup_columns(struct libscols_table *tb, int notree) + return; + fail: + scols_unref_table(tb); +- err(EXIT_FAILURE, "faild to create output columns"); ++ err(EXIT_FAILURE, "failed to create output columns"); + } + + /* add a new line to @tb, the content is based on @st */ +@@ -104,7 +104,7 @@ fail: + return -1; + } + +-/* read all entrines from directory addressed by @fd */ ++/* read all entries from directory addressed by @fd */ + static int add_children(struct libscols_table *tb, + struct libscols_line *ln, + int fd) +@@ -142,12 +142,15 @@ static void add_lines(struct libscols_table *tb, const char *dirname) + static void __attribute__((__noreturn__)) usage(FILE *out) + { + fprintf(out, " %s [options] [<dir> ...]\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); ++ 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(" -J, --json use JSON output format\n", out); ++ fputs(" -r, --raw use raw output format\n", out); ++ fputs(" -S, --range-start <n> first line to print\n", out); ++ fputs(" -E, --range-end <n> last line to print\n", out); + + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); + } +@@ -155,17 +158,20 @@ static void __attribute__((__noreturn__)) usage(FILE *out) + int main(int argc, char *argv[]) + { + struct libscols_table *tb; +- int c, notree = 0; ++ int c, notree = 0, nstart = -1, nend = -1; ++ + + 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 }, ++ { "ascii", 0, NULL, 'i' }, ++ { "csv", 0, NULL, 'c' }, ++ { "list", 0, NULL, 'l' }, ++ { "noheadings", 0, NULL, 'n' }, ++ { "pairs", 0, NULL, 'p' }, ++ { "json", 0, NULL, 'J' }, ++ { "raw", 0, NULL, 'r' }, ++ { "range-start",1, NULL, 'S' }, ++ { "range-end", 1, NULL, 'E' }, ++ { NULL, 0, NULL, 0 }, + }; + + setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */ +@@ -174,9 +180,9 @@ int main(int argc, char *argv[]) + + tb = scols_new_table(); + if (!tb) +- err(EXIT_FAILURE, "faild to create output table"); ++ err(EXIT_FAILURE, "failed to create output table"); + +- while((c = getopt_long(argc, argv, "cilnpr", longopts, NULL)) != -1) { ++ while((c = getopt_long(argc, argv, "ciJlnprS:E:", longopts, NULL)) != -1) { + switch(c) { + case 'c': + scols_table_set_column_separator(tb, ","); +@@ -186,6 +192,10 @@ int main(int argc, char *argv[]) + case 'i': + scols_table_enable_ascii(tb, 1); + break; ++ case 'J': ++ scols_table_set_name(tb, "scolstest"); ++ scols_table_enable_json(tb, 1); ++ break; + case 'l': + notree = 1; + break; +@@ -200,19 +210,40 @@ int main(int argc, char *argv[]) + scols_table_enable_raw(tb, 1); + notree = 1; + break; ++ case 'S': ++ nstart = strtos32_or_err(optarg, "failed to parse range start") - 1; ++ break; ++ case 'E': ++ nend = strtos32_or_err(optarg, "failed to parse range end") - 1; ++ break; + default: + usage(stderr); + } + } + +- scols_table_enable_colors(tb, 1); ++ scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); + setup_columns(tb, notree); + +- while (optind < argc) ++ if (optind == argc) ++ add_lines(tb, "."); ++ else while (optind < argc) + add_lines(tb, argv[optind++]); + +- scols_print_table(tb); +- scols_unref_table(tb); ++ if (nstart >= 0 || nend >= 0) { ++ /* print subset */ ++ struct libscols_line *start = NULL, *end = NULL; ++ ++ if (nstart >= 0) ++ start = scols_table_get_line(tb, nstart); ++ if (nend >= 0) ++ end = scols_table_get_line(tb, nend); + ++ if (start || end) ++ scols_table_print_range(tb, start, end); ++ } else ++ /* print all table */ ++ scols_print_table(tb); ++ ++ scols_unref_table(tb); + return EXIT_SUCCESS; + } +diff --git a/libsmartcols/samples/wrap.c b/libsmartcols/samples/wrap.c +new file mode 100644 +index 000000000..795bef714 +--- /dev/null ++++ b/libsmartcols/samples/wrap.c +@@ -0,0 +1,111 @@ ++/* ++ * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com> ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++#include <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <errno.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <dirent.h> ++#include <getopt.h> ++ ++#include "c.h" ++#include "nls.h" ++#include "strutils.h" ++#include "xalloc.h" ++ ++#include "libsmartcols.h" ++ ++ ++enum { COL_NAME, COL_DESC, COL_FOO, COL_LIKE, COL_TEXT }; ++ ++/* add columns to the @tb */ ++static void setup_columns(struct libscols_table *tb) ++{ ++ if (!scols_table_new_column(tb, "NAME", 0, SCOLS_FL_TREE)) ++ goto fail; ++ if (!scols_table_new_column(tb, "DESC", 0, 0)) ++ goto fail; ++ if (!scols_table_new_column(tb, "FOO", 0, SCOLS_FL_WRAP)) ++ goto fail; ++ if (!scols_table_new_column(tb, "LIKE", 0, SCOLS_FL_RIGHT)) ++ goto fail; ++ if (!scols_table_new_column(tb, "TEXT", 0, SCOLS_FL_WRAP)) ++ goto fail; ++ return; ++fail: ++ scols_unref_table(tb); ++ err(EXIT_FAILURE, "failed to create output columns"); ++} ++ ++static char *gen_text(const char *prefix, const char *sub_prefix, char *buf, size_t sz) ++{ ++ int x = snprintf(buf, sz, "%s-%s-", prefix, sub_prefix); ++ ++ for ( ; (size_t)x < sz - 1; x++) ++ buf[x] = *prefix; ++ ++ buf[x++] = 'x'; ++ buf[x] = '\0'; ++ return buf; ++} ++ ++static struct libscols_line * add_line( struct libscols_table *tb, ++ struct libscols_line *parent, ++ const char *prefix) ++{ ++ char buf[BUFSIZ]; ++ struct libscols_line *ln = scols_table_new_line(tb, parent); ++ if (!ln) ++ err(EXIT_FAILURE, "failed to create output line"); ++ ++ if (scols_line_set_data(ln, COL_NAME, gen_text(prefix, "N", buf, 15))) ++ goto fail; ++ if (scols_line_set_data(ln, COL_DESC, gen_text(prefix, "D", buf, 10))) ++ goto fail; ++ if (scols_line_set_data(ln, COL_FOO, gen_text(prefix, "U", buf, 55))) ++ goto fail; ++ if (scols_line_set_data(ln, COL_LIKE, "1")) ++ goto fail; ++ if (scols_line_set_data(ln, COL_TEXT, gen_text(prefix, "T", buf, 50))) ++ goto fail; ++ return ln; ++fail: ++ scols_unref_table(tb); ++ err(EXIT_FAILURE, "failed to create output line"); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ struct libscols_table *tb; ++ struct libscols_line *ln, *xln; ++ ++ setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */ ++ ++ scols_init_debug(0); ++ ++ tb = scols_new_table(); ++ if (!tb) ++ err(EXIT_FAILURE, "failed to create output table"); ++ ++ scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); ++ setup_columns(tb); ++ ++ ln = add_line(tb, NULL, "A"); ++ add_line(tb, ln, "aa"); ++ add_line(tb, ln, "ab"); ++ ++ ln = add_line(tb, NULL, "B"); ++ xln = add_line(tb, ln, "ba"); ++ add_line(tb, xln, "baa"); ++ add_line(tb, xln, "bab"); ++ add_line(tb, ln, "bb"); ++ ++ scols_print_table(tb); ++ scols_unref_table(tb); ++ return EXIT_SUCCESS; ++} +diff --git a/libsmartcols/src/Makemodule.am b/libsmartcols/src/Makemodule.am +index bfe8c75c1..952d5e58f 100644 +--- a/libsmartcols/src/Makemodule.am ++++ b/libsmartcols/src/Makemodule.am +@@ -1,10 +1,10 @@ + + +-## 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 ++# smartcols.h is generated, so it's stored in builddir! ++smartcolsincdir = $(includedir)/libsmartcols ++nodist_smartcolsinc_HEADERS = libsmartcols/src/libsmartcols.h + +-noinst_LTLIBRARIES += libsmartcols.la ++usrlib_exec_LTLIBRARIES += libsmartcols.la + libsmartcols_la_SOURCES= \ + include/list.h \ + \ +@@ -17,49 +17,32 @@ libsmartcols_la_SOURCES= \ + libsmartcols/src/table.c \ + libsmartcols/src/table_print.c \ + libsmartcols/src/version.c \ +- libsmartcols/src/init.c \ +- $(nodist_smartcolsinc_HEADERS) ++ libsmartcols/src/init.c + +-nodist_libsmartcols_la_SOURCES = libsmartcols/src/smartcolsP.h +- +-libsmartcols_la_LIBADD = libcommon.la ++libsmartcols_la_LIBADD = $(LDADD) libcommon.la + + libsmartcols_la_CFLAGS = \ ++ $(AM_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 ++EXTRA_libsmartcols_la_DEPENDENCIES = \ ++ libsmartcols/src/libsmartcols.sym + + 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 + ++EXTRA_DIST += \ ++ libsmartcols/src/libsmartcols.sym + + # 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); \ ++ $(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'); \ +diff --git a/libsmartcols/src/cell.c b/libsmartcols/src/cell.c +index ea41f698f..0717a2d09 100644 +--- a/libsmartcols/src/cell.c ++++ b/libsmartcols/src/cell.c +@@ -11,7 +11,7 @@ + /** + * SECTION: cell + * @title: Cell +- * @short_description: cell API ++ * @short_description: container for your data + * + * 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 +@@ -41,8 +41,6 @@ + */ + int scols_reset_cell(struct libscols_cell *ce) + { +- assert(ce); +- + if (!ce) + return -EINVAL; + +@@ -56,34 +54,21 @@ int scols_reset_cell(struct libscols_cell *ce) + /** + * scols_cell_set_data: + * @ce: a pointer to a struct libscols_cell instance +- * @str: data (used for scols_printtable()) ++ * @data: data (used for scols_print_table()) + * +- * Stores a copy of the @str in @ce. ++ * Stores a copy of the @str in @ce, the old data are deallocated by free(). + * + * Returns: 0, a negative value in case of an error. + */ +-int scols_cell_set_data(struct libscols_cell *ce, const char *str) ++int scols_cell_set_data(struct libscols_cell *ce, const char *data) + { +- 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; ++ return strdup_to_struct_member(ce, data, data); + } + + /** + * scols_cell_refer_data: + * @ce: a pointer to a struct libscols_cell instance +- * @str: data (used for scols_printtable()) ++ * @data: data (used for scols_print_table()) + * + * Adds a reference to @str to @ce. The pointer is deallocated by + * scols_reset_cell() or scols_unref_line(). This function is mostly designed +@@ -92,14 +77,12 @@ int scols_cell_set_data(struct libscols_cell *ce, const char *str) + * + * Returns: 0, a negative value in case of an error. + */ +-int scols_cell_refer_data(struct libscols_cell *ce, char *str) ++int scols_cell_refer_data(struct libscols_cell *ce, char *data) + { +- assert(ce); +- + if (!ce) + return -EINVAL; + free(ce->data); +- ce->data = str; ++ ce->data = data; + return 0; + } + +@@ -111,7 +94,6 @@ int scols_cell_refer_data(struct libscols_cell *ce, char *str) + */ + const char *scols_cell_get_data(const struct libscols_cell *ce) + { +- assert(ce); + return ce ? ce->data : NULL; + } + +@@ -124,8 +106,6 @@ const char *scols_cell_get_data(const struct libscols_cell *ce) + */ + int scols_cell_set_userdata(struct libscols_cell *ce, void *data) + { +- assert(ce); +- + if (!ce) + return -EINVAL; + ce->userdata = data; +@@ -140,7 +120,7 @@ int scols_cell_set_userdata(struct libscols_cell *ce, void *data) + */ + void *scols_cell_get_userdata(struct libscols_cell *ce) + { +- return ce ? ce->userdata : NULL; ++ return ce->userdata; + } + + /** +@@ -178,7 +158,7 @@ int scols_cmpstr_cells(struct libscols_cell *a, + /** + * scols_cell_set_color: + * @ce: a pointer to a struct libscols_cell instance +- * @color: ESC sequence ++ * @color: color name or ESC sequence + * + * Set the color of @ce to @color. + * +@@ -186,32 +166,70 @@ int scols_cmpstr_cells(struct libscols_cell *a, + */ + int scols_cell_set_color(struct libscols_cell *ce, const char *color) + { +- char *p = NULL; ++ if (color && isalpha(*color)) { ++ color = color_sequence_from_colorname(color); ++ if (!color) ++ return -EINVAL; ++ } ++ return strdup_to_struct_member(ce, color, color); ++} + +- assert(ce); ++/** ++ * 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) ++{ ++ return ce->color; ++} + ++/** ++ * scols_cell_set_flags: ++ * @ce: a pointer to a struct libscols_cell instance ++ * @flags: SCOLS_CELL_FL_* flags ++ * ++ * Note that cells in the table are always aligned by column flags. The cell ++ * flags are used for table title only (now). ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_cell_set_flags(struct libscols_cell *ce, int flags) ++{ + if (!ce) + return -EINVAL; +- if (color) { +- p = strdup(color); +- if (!p) +- return -ENOMEM; +- } +- free(ce->color); +- ce->color = p; ++ ce->flags = flags; + return 0; + } + + /** +- * scols_cell_get_color: ++ * scols_cell_get_flags: + * @ce: a pointer to a struct libscols_cell instance + * +- * Returns: the current color of @ce. ++ * Returns: the current flags + */ +-const char *scols_cell_get_color(const struct libscols_cell *ce) ++int scols_cell_get_flags(const struct libscols_cell *ce) + { +- assert(ce); +- return ce ? ce->color : NULL; ++ return ce->flags; ++} ++ ++/** ++ * scols_cell_get_alignment: ++ * @ce: a pointer to a struct libscols_cell instance ++ * ++ * Since: 2.30 ++ * ++ * Returns: SCOLS_CELL_FL_{RIGHT,CELNTER,LEFT} ++ */ ++int scols_cell_get_alignment(const struct libscols_cell *ce) ++{ ++ if (ce->flags & SCOLS_CELL_FL_RIGHT) ++ return SCOLS_CELL_FL_RIGHT; ++ else if (ce->flags & SCOLS_CELL_FL_CENTER) ++ return SCOLS_CELL_FL_CENTER; ++ ++ return SCOLS_CELL_FL_LEFT; /* default */ + } + + /** +@@ -228,15 +246,12 @@ int scols_cell_copy_content(struct libscols_cell *dest, + { + 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)); ++ DBG(CELL, ul_debugobj(src, "copy")); + return rc; + } +diff --git a/libsmartcols/src/column.c b/libsmartcols/src/column.c +index d1d10a6d0..e9d6dc404 100644 +--- a/libsmartcols/src/column.c ++++ b/libsmartcols/src/column.c +@@ -11,7 +11,7 @@ + /** + * SECTION: column + * @title: Column +- * @short_description: column API ++ * @short_description: defines output columns formats, headers, etc. + * + * An API to access and modify per-column data and information. + */ +@@ -22,6 +22,8 @@ + #include <string.h> + #include <ctype.h> + ++#include "mbsalign.h" ++ + #include "smartcolsP.h" + + /** +@@ -29,7 +31,7 @@ + * + * Allocates space for a new column. + * +- * Returns: a pointer to a new struct libscols_cell instance, NULL in case of an ENOMEM error. ++ * Returns: a pointer to a new struct libscols_column instance, NULL in case of an ENOMEM error. + */ + struct libscols_column *scols_new_column(void) + { +@@ -70,6 +72,8 @@ void scols_unref_column(struct libscols_column *cl) + list_del(&cl->cl_columns); + scols_reset_cell(&cl->header); + free(cl->color); ++ free(cl->safechars); ++ free(cl->pending_data_buf); + free(cl); + } + } +@@ -86,14 +90,13 @@ 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)); ++ DBG(COL, ul_debugobj(cl, "copy")); + + if (scols_column_set_color(ret, cl->color)) + goto err; +@@ -119,14 +122,12 @@ err: + * @cl: a pointer to a struct libscols_column instance + * @whint: a width hint + * +- * Sets the width hint of column @cl to @whint. ++ * Sets the width hint of column @cl to @whint. See scols_table_new_column(). + * + * 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; + +@@ -140,10 +141,9 @@ int scols_column_set_whint(struct libscols_column *cl, double whint) + * + * Returns: The width hint of column @cl, a negative value in case of an error. + */ +-double scols_column_get_whint(struct libscols_column *cl) ++double scols_column_get_whint(const struct libscols_column *cl) + { +- assert(cl); +- return cl ? cl->width_hint : -EINVAL; ++ return cl->width_hint; + } + + /** +@@ -157,25 +157,78 @@ double scols_column_get_whint(struct libscols_column *cl) + */ + int scols_column_set_flags(struct libscols_column *cl, int flags) + { +- assert(cl); +- + if (!cl) + return -EINVAL; + ++ if (cl->table) { ++ if (!(cl->flags & SCOLS_FL_TREE) && (flags & SCOLS_FL_TREE)) ++ cl->table->ntreecols++; ++ else if ((cl->flags & SCOLS_FL_TREE) && !(flags & SCOLS_FL_TREE)) ++ cl->table->ntreecols--; ++ } ++ + cl->flags = flags; + return 0; + } + ++/** ++ * scols_column_set_json_type: ++ * @cl: a pointer to a struct libscols_column instance ++ * @type: SCOLS_JSON_* type ++ * ++ * Sets the type used for JSON formatting, the default is SCOLS_JSON_STRING. ++ * ++ * Returns: 0, a negative value in case of an error. ++ * ++ * Since: 2.33 ++ */ ++int scols_column_set_json_type(struct libscols_column *cl, int type) ++{ ++ if (!cl) ++ return -EINVAL; ++ ++ cl->json_type = type; ++ return 0; ++ ++} ++ ++/** ++ * scols_column_get_json_type: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Note that SCOLS_JSON_BOOLEAN interprets NULL, empty strings, '0', 'N' and ++ * 'n' as "false"; and everything else as "true". ++ * ++ * Returns: JSON type used for formatting or a negative value in case of an error. ++ * ++ * Since: 2.33 ++ */ ++int scols_column_get_json_type(const struct libscols_column *cl) ++{ ++ return cl ? cl->json_type : -EINVAL; ++} ++ ++ ++/** ++ * scols_column_get_table: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Returns: pointer to the table where columns is used ++ */ ++struct libscols_table *scols_column_get_table(const struct libscols_column *cl) ++{ ++ return cl->table; ++} ++ + /** + * 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) ++int scols_column_get_flags(const struct libscols_column *cl) + { +- assert(cl); +- return cl ? cl->flags : -EINVAL; ++ return cl->flags; + } + + /** +@@ -187,14 +240,13 @@ int scols_column_get_flags(struct libscols_column *cl) + */ + struct libscols_cell *scols_column_get_header(struct libscols_column *cl) + { +- assert(cl); +- return cl ? &cl->header : NULL; ++ return &cl->header; + } + + /** + * scols_column_set_color: + * @cl: a pointer to a struct libscols_column instance +- * @color: ESC sequence ++ * @color: color name or ESC sequence + * + * The default color for data cells and column header. + * +@@ -208,20 +260,12 @@ struct libscols_cell *scols_column_get_header(struct libscols_column *cl) + */ + 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; ++ if (color && isalpha(*color)) { ++ color = color_sequence_from_colorname(color); ++ if (!color) ++ return -EINVAL; + } +- +- free(cl->color); +- cl->color = p; +- return 0; ++ return strdup_to_struct_member(cl, color, color); + } + + /** +@@ -230,12 +274,79 @@ int scols_column_set_color(struct libscols_column *cl, const char *color) + * + * Returns: The current color setting of the column @cl. + */ +-const char *scols_column_get_color(struct libscols_column *cl) ++const char *scols_column_get_color(const struct libscols_column *cl) ++{ ++ return cl->color; ++} ++ ++/** ++ * scols_wrapnl_nextchunk: ++ * @cl: a pointer to a struct libscols_column instance ++ * @data: string ++ * @userdata: callback private data ++ * ++ * This is built-in function for scols_column_set_wrapfunc(). This function ++ * terminates the current chunk by \0 and returns pointer to the begin of ++ * the next chunk. The chunks are based on \n. ++ * ++ * For example for data "AAA\nBBB\nCCC" the next chunk is "BBB". ++ * ++ * Returns: next chunk ++ * ++ * Since: 2.29 ++ */ ++char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unused)), ++ char *data, ++ void *userdata __attribute__((unused))) + { +- assert(cl); +- return cl ? cl->color : NULL; ++ char *p = data ? strchr(data, '\n') : NULL; ++ ++ if (p) { ++ *p = '\0'; ++ return p + 1; ++ } ++ return NULL; + } + ++/** ++ * scols_wrapnl_chunksize: ++ * @cl: a pointer to a struct libscols_column instance ++ * @data: string ++ * @userdata: callback private data ++ * ++ * Analyzes @data and returns size of the largest chunk. The chunks are based ++ * on \n. For example for data "AAA\nBBB\nCCCC" the largest chunk size is 4. ++ * ++ * Note that the size has to be based on number of terminal cells rather than ++ * bytes to support multu-byte output. ++ * ++ * Returns: size of the largest chunk. ++ * ++ * Since: 2.29 ++ */ ++size_t scols_wrapnl_chunksize(const struct libscols_column *cl __attribute__((unused)), ++ const char *data, ++ void *userdata __attribute__((unused))) ++{ ++ size_t sum = 0; ++ ++ while (data && *data) { ++ const char *p; ++ size_t sz; ++ ++ p = strchr(data, '\n'); ++ if (p) { ++ sz = mbs_safe_nwidth(data, p - data, NULL); ++ p++; ++ } else ++ sz = mbs_safe_width(data); ++ ++ sum = max(sum, sz); ++ data = p; ++ } ++ ++ return sum; ++} + + /** + * scols_column_set_cmpfunc: +@@ -251,7 +362,6 @@ int scols_column_set_cmpfunc(struct libscols_column *cl, + void *), + void *data) + { +- assert(cl); + if (!cl) + return -EINVAL; + +@@ -261,19 +371,114 @@ int scols_column_set_cmpfunc(struct libscols_column *cl, + } + + /** +- * scols_column_is_trunc: ++ * scols_column_set_wrapfunc: + * @cl: a pointer to a struct libscols_column instance ++ * @wrap_chunksize: function to return size of the largest chink of data ++ * @wrap_nextchunk: function to return next zero terminated data ++ * @userdata: optional stuff for callbacks + * +- * Gets the value of @cl's flag trunc. ++ * Extends SCOLS_FL_WRAP and allows to set custom wrap function. The default ++ * is to wrap by column size, but you can create functions to wrap for example ++ * after \n or after words, etc. ++ * ++ * Returns: 0, a negative value in case of an error. + * +- * Returns: trunc flag value, negative value in case of an error. ++ * Since: 2.29 + */ +-int scols_column_is_trunc(struct libscols_column *cl) ++int scols_column_set_wrapfunc(struct libscols_column *cl, ++ size_t (*wrap_chunksize)(const struct libscols_column *, ++ const char *, ++ void *), ++ char * (*wrap_nextchunk)(const struct libscols_column *, ++ char *, ++ void *), ++ void *userdata) + { +- assert(cl); + if (!cl) + return -EINVAL; +- return cl->flags & SCOLS_FL_TRUNC; ++ ++ cl->wrap_nextchunk = wrap_nextchunk; ++ cl->wrap_chunksize = wrap_chunksize; ++ cl->wrapfunc_data = userdata; ++ return 0; ++} ++ ++/** ++ * scols_column_set_safechars: ++ * @cl: a pointer to a struct libscols_column instance ++ * @safe: safe characters (e.g. "\n\t") ++ * ++ * Use for bytes you don't want to encode on output. This is for example ++ * necessary if you want to use custom wrap function based on \n, in this case ++ * you have to set "\n" as a safe char. ++ * ++ * Returns: 0, a negative value in case of an error. ++ * ++ * Since: 2.29 ++ */ ++int scols_column_set_safechars(struct libscols_column *cl, const char *safe) ++{ ++ return strdup_to_struct_member(cl, safechars, safe); ++} ++ ++/** ++ * scols_column_get_safechars: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Returns: safe chars ++ * ++ * Since: 2.29 ++ */ ++const char *scols_column_get_safechars(const struct libscols_column *cl) ++{ ++ return cl->safechars; ++} ++ ++/** ++ * scols_column_get_width: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Important note: the column width is unknown until library starts printing ++ * (width is calculated before printing). The function is usable for example in ++ * nextchunk() callback specified by scols_column_set_wrapfunc(). ++ * ++ * See also scols_column_get_whint(), it returns wanted size (!= final size). ++ * ++ * Returns: column width ++ * ++ * Since: 2.29 ++ */ ++size_t scols_column_get_width(const struct libscols_column *cl) ++{ ++ return cl->width; ++} ++ ++/** ++ * scols_column_is_hidden: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Gets the value of @cl's flag hidden. ++ * ++ * Returns: 0 or 1 ++ * ++ * Since: 2.27 ++ */ ++int scols_column_is_hidden(const struct libscols_column *cl) ++{ ++ return cl->flags & SCOLS_FL_HIDDEN ? 1 : 0; ++} ++ ++/** ++ * scols_column_is_trunc: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Gets the value of @cl's flag trunc. ++ * ++ * Returns: 0 or 1 ++ */ ++int scols_column_is_trunc(const struct libscols_column *cl) ++{ ++ return cl->flags & SCOLS_FL_TRUNC ? 1 : 0; + } + /** + * scols_column_is_tree: +@@ -281,14 +486,11 @@ int scols_column_is_trunc(struct libscols_column *cl) + * + * Gets the value of @cl's flag tree. + * +- * Returns: tree flag value, negative value in case of an error. ++ * Returns: 0 or 1 + */ +-int scols_column_is_tree(struct libscols_column *cl) ++int scols_column_is_tree(const struct libscols_column *cl) + { +- assert(cl); +- if (!cl) +- return -EINVAL; +- return cl->flags & SCOLS_FL_TREE; ++ return cl->flags & SCOLS_FL_TREE ? 1 : 0; + } + /** + * scols_column_is_right: +@@ -296,14 +498,11 @@ int scols_column_is_tree(struct libscols_column *cl) + * + * Gets the value of @cl's flag right. + * +- * Returns: right flag value, negative value in case of an error. ++ * Returns: 0 or 1 + */ +-int scols_column_is_right(struct libscols_column *cl) ++int scols_column_is_right(const struct libscols_column *cl) + { +- assert(cl); +- if (!cl) +- return -EINVAL; +- return cl->flags & SCOLS_FL_RIGHT; ++ return cl->flags & SCOLS_FL_RIGHT ? 1 : 0; + } + /** + * scols_column_is_strict_width: +@@ -311,14 +510,11 @@ int scols_column_is_right(struct libscols_column *cl) + * + * Gets the value of @cl's flag strict_width. + * +- * Returns: strict_width flag value, negative value in case of an error. ++ * Returns: 0 or 1 + */ +-int scols_column_is_strict_width(struct libscols_column *cl) ++int scols_column_is_strict_width(const struct libscols_column *cl) + { +- assert(cl); +- if (!cl) +- return -EINVAL; +- return cl->flags & SCOLS_FL_STRICTWIDTH; ++ return cl->flags & SCOLS_FL_STRICTWIDTH ? 1 : 0; + } + /** + * scols_column_is_noextremes: +@@ -326,12 +522,37 @@ int scols_column_is_strict_width(struct libscols_column *cl) + * + * Gets the value of @cl's flag no_extremes. + * +- * Returns: no_extremes flag value, negative value in case of an error. ++ * Returns: 0 or 1 + */ +-int scols_column_is_noextremes(struct libscols_column *cl) ++int scols_column_is_noextremes(const struct libscols_column *cl) + { +- assert(cl); +- if (!cl) +- return -EINVAL; +- return cl->flags & SCOLS_FL_NOEXTREMES; ++ return cl->flags & SCOLS_FL_NOEXTREMES ? 1 : 0; ++} ++/** ++ * scols_column_is_wrap: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Gets the value of @cl's flag wrap. ++ * ++ * Returns: 0 or 1 ++ * ++ * Since: 2.28 ++ */ ++int scols_column_is_wrap(const struct libscols_column *cl) ++{ ++ return cl->flags & SCOLS_FL_WRAP ? 1 : 0; ++} ++/** ++ * scols_column_is_customwrap: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Returns: 0 or 1 ++ * ++ * Since: 2.29 ++ */ ++int scols_column_is_customwrap(const struct libscols_column *cl) ++{ ++ return (cl->flags & SCOLS_FL_WRAP) ++ && cl->wrap_chunksize ++ && cl->wrap_nextchunk ? 1 : 0; + } +diff --git a/libsmartcols/src/iter.c b/libsmartcols/src/iter.c +index 72c7865a8..91cc08009 100644 +--- a/libsmartcols/src/iter.c ++++ b/libsmartcols/src/iter.c +@@ -68,7 +68,7 @@ void scols_reset_iter(struct libscols_iter *itr, int direction) + * + * Returns: SCOLS_INTER_{FOR,BACK}WARD + */ +-int scols_iter_get_direction(struct libscols_iter *itr) ++int scols_iter_get_direction(const struct libscols_iter *itr) + { + return itr->direction; + } +diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in +index e61256022..f8be0bc04 100644 +--- a/libsmartcols/src/libsmartcols.h.in ++++ b/libsmartcols/src/libsmartcols.h.in +@@ -83,12 +83,33 @@ enum { + 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*/ ++ SCOLS_FL_HIDDEN = (1 << 5), /* maintain data, but don't print */ ++ SCOLS_FL_WRAP = (1 << 6) /* wrap long lines to multi-line cells */ ++}; ++ ++/* ++ * Column JSON types ++ */ ++enum { ++ SCOLS_JSON_STRING = 0, /* default */ ++ SCOLS_JSON_NUMBER = 1, ++ SCOLS_JSON_BOOLEAN = 2 ++}; ++ ++/* ++ * Cell flags, see scols_cell_set_flags() before use ++ */ ++enum { ++ /* alignment evaluated in order: right,center,left */ ++ SCOLS_CELL_FL_LEFT = 0, ++ SCOLS_CELL_FL_CENTER = (1 << 0), ++ SCOLS_CELL_FL_RIGHT = (1 << 1) + }; + + 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); ++extern int scols_iter_get_direction(const struct libscols_iter *itr); + + /* init.c */ + extern void scols_init_debug(int mask); +@@ -101,50 +122,78 @@ extern int scols_get_library_version(const char **ver_string); + 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); ++extern struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sy); ++extern int scols_symbols_set_branch(struct libscols_symbols *sy, const char *str); ++extern int scols_symbols_set_vertical(struct libscols_symbols *sy, const char *str); ++extern int scols_symbols_set_right(struct libscols_symbols *sy, const char *str); ++extern int scols_symbols_set_title_padding(struct libscols_symbols *sy, const char *str); ++extern int scols_symbols_set_cell_padding(struct libscols_symbols *sy, 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 int scols_cell_set_data(struct libscols_cell *ce, const char *data); ++extern int scols_cell_refer_data(struct libscols_cell *ce, char *data); + 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 int scols_cell_set_flags(struct libscols_cell *ce, int flags); ++extern int scols_cell_get_flags(const struct libscols_cell *ce); ++extern int scols_cell_get_alignment(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_is_tree(const struct libscols_column *cl); ++extern int scols_column_is_trunc(const struct libscols_column *cl); ++extern int scols_column_is_right(const struct libscols_column *cl); ++extern int scols_column_is_strict_width(const struct libscols_column *cl); ++extern int scols_column_is_hidden(const struct libscols_column *cl); ++extern int scols_column_is_noextremes(const struct libscols_column *cl); ++extern int scols_column_is_wrap(const struct libscols_column *cl); ++extern int scols_column_is_customwrap(const struct libscols_column *cl); ++ ++extern size_t scols_column_get_width(const struct libscols_column *cl); ++ ++extern int scols_column_set_safechars(struct libscols_column *cl, const char *safe); ++extern const char *scols_column_get_safechars(const struct libscols_column *cl); ++ ++extern int scols_column_set_json_type(struct libscols_column *cl, int type); ++extern int scols_column_get_json_type(const 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 int scols_column_get_flags(const 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 double scols_column_get_whint(const 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 const char *scols_column_get_color(const struct libscols_column *cl); ++extern struct libscols_table *scols_column_get_table(const 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); + ++extern int scols_column_set_wrapfunc(struct libscols_column *cl, ++ size_t (*wrap_chunksize)(const struct libscols_column *, ++ const char *, void *), ++ char * (*wrap_nextchunk)(const struct libscols_column *, ++ char *, void *), ++ void *userdata); ++ ++extern char *scols_wrapnl_nextchunk(const struct libscols_column *cl, char *data, void *userdata); ++extern size_t scols_wrapnl_chunksize(const struct libscols_column *cl, const char *data, void *userdata); ++ + /* line.c */ + extern struct libscols_line *scols_new_line(void); + extern void scols_ref_line(struct libscols_line *ln); +@@ -156,36 +205,52 @@ 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_is_ancestor(struct libscols_line *ln, struct libscols_line *parent); + 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 struct libscols_line *scols_line_get_parent(const 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 const char *scols_line_get_color(const struct libscols_line *ln); ++extern size_t scols_line_get_ncells(const 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); ++extern int scols_line_set_column_data(struct libscols_line *ln, struct libscols_column *cl, const char *data); ++extern int scols_line_refer_column_data(struct libscols_line *ln, struct libscols_column *cl, char *data); ++extern struct libscols_line *scols_copy_line(const 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_colors_wanted(const struct libscols_table *tb); ++extern int scols_table_set_name(struct libscols_table *tb, const char *name); ++extern const char *scols_table_get_name(const struct libscols_table *tb); ++extern struct libscols_cell *scols_table_get_title(struct libscols_table *tb); ++extern int scols_table_is_raw(const struct libscols_table *tb); ++extern int scols_table_is_ascii(const struct libscols_table *tb); ++extern int scols_table_is_json(const struct libscols_table *tb); ++extern int scols_table_is_noheadings(const struct libscols_table *tb); ++extern int scols_table_is_header_repeat(const struct libscols_table *tb); ++extern int scols_table_is_empty(const struct libscols_table *tb); ++extern int scols_table_is_export(const struct libscols_table *tb); ++extern int scols_table_is_maxout(const struct libscols_table *tb); ++extern int scols_table_is_nowrap(const struct libscols_table *tb); ++extern int scols_table_is_nolinesep(const struct libscols_table *tb); ++extern int scols_table_is_tree(const struct libscols_table *tb); ++extern int scols_table_is_noencoding(const 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_json(struct libscols_table *tb, int enable); + extern int scols_table_enable_noheadings(struct libscols_table *tb, int enable); ++extern int scols_table_enable_header_repeat(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_enable_nowrap(struct libscols_table *tb, int enable); ++extern int scols_table_enable_nolinesep(struct libscols_table *tb, int enable); ++extern int scols_table_enable_noencoding(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); +@@ -196,12 +261,13 @@ 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 int scols_table_move_column(struct libscols_table *tb, struct libscols_column *pre, struct libscols_column *cl); + 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 const char *scols_table_get_column_separator(const struct libscols_table *tb); ++extern const char *scols_table_get_line_separator(const struct libscols_table *tb); ++extern size_t scols_table_get_ncols(const struct libscols_table *tb); ++extern size_t scols_table_get_nlines(const 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); +@@ -211,17 +277,43 @@ extern struct libscols_line *scols_table_new_line(struct libscols_table *tb, str + 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_default_symbols(struct libscols_table *tb); ++extern struct libscols_symbols *scols_table_get_symbols(const struct libscols_table *tb); + + extern int scols_table_set_stream(struct libscols_table *tb, FILE *stream); +-extern FILE *scols_table_get_stream(struct libscols_table *tb); ++extern FILE *scols_table_get_stream(const 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); ++extern int scols_sort_table_by_tree(struct libscols_table *tb); ++/* ++ * ++ */ ++enum { ++ SCOLS_TERMFORCE_AUTO = 0, ++ SCOLS_TERMFORCE_NEVER, ++ SCOLS_TERMFORCE_ALWAYS ++}; ++extern int scols_table_set_termforce(struct libscols_table *tb, int force); ++extern int scols_table_get_termforce(const struct libscols_table *tb); ++extern int scols_table_set_termwidth(struct libscols_table *tb, size_t width); ++extern size_t scols_table_get_termwidth(const struct libscols_table *tb); ++extern int scols_table_set_termheight(struct libscols_table *tb, size_t height); ++extern size_t scols_table_get_termheight(const struct libscols_table *tb); ++ + + /* 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); + ++extern int scols_table_print_range( struct libscols_table *tb, ++ struct libscols_line *start, ++ struct libscols_line *end); ++extern int scols_table_print_range_to_string( struct libscols_table *tb, ++ struct libscols_line *start, ++ struct libscols_line *end, ++ char **data); ++ + #ifdef __cplusplus + } + #endif +diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym +index a4de524f4..331a55554 100644 +--- a/libsmartcols/src/libsmartcols.sym ++++ b/libsmartcols/src/libsmartcols.sym +@@ -1,5 +1,7 @@ + /* + * symbols since util-linux 2.25 ++ * ++ * Copyright (C) 2014-2016 Karel Zak <kzak@redhat.com> + */ + SMARTCOLS_2.25 { + global: +@@ -110,3 +112,73 @@ global: + local: + *; + }; ++ ++SMARTCOLS_2.27 { ++global: ++ scols_column_is_hidden; ++ scols_table_enable_json; ++ scols_table_is_json; ++ scols_table_set_name; ++} SMARTCOLS_2.25; ++ ++SMARTCOLS_2.28 { ++global: ++ scols_column_is_wrap; ++ scols_line_refer_column_data; ++ scols_line_set_column_data; ++ scols_symbols_set_title_padding; ++ scols_table_enable_nowrap; ++ scols_table_get_title; ++ scols_cell_get_flags; ++ scols_cell_set_flags; ++ scols_table_print_range; ++ scols_table_print_range_to_string; ++ scols_table_enable_nolinesep; ++} SMARTCOLS_2.27; ++ ++SMARTCOLS_2.29 { ++global: ++ scols_column_get_safechars; ++ scols_column_get_table; ++ scols_column_get_width; ++ scols_column_is_customwrap; ++ scols_column_set_safechars; ++ scols_column_set_wrapfunc; ++ scols_symbols_set_cell_padding; ++ scols_table_get_name; ++ scols_table_get_symbols; ++ scols_table_get_termforce; ++ scols_table_get_termwidth; ++ scols_table_is_nolinesep; ++ scols_table_is_nowrap; ++ scols_table_set_default_symbols; ++ scols_table_set_termforce; ++ scols_table_set_termwidth; ++ scols_wrapnl_chunksize; ++ scols_wrapnl_nextchunk; ++} SMARTCOLS_2.28; ++ ++ ++SMARTCOLS_2.30 { ++global: ++ scols_cell_get_alignment; ++ scols_table_move_column; ++ scols_sort_table_by_tree; ++ scols_line_is_ancestor; ++} SMARTCOLS_2.29; ++ ++ ++SMARTCOLS_2.31 { ++ scols_table_set_termheight; ++ scols_table_get_termheight; ++ scols_table_is_header_repeat; ++ scols_table_enable_header_repeat; ++ scols_table_enable_noencoding; ++ scols_table_is_noencoding; ++} SMARTCOLS_2.30; ++ ++ ++SMARTCOLS_2.33 { ++ scols_column_set_json_type; ++ scols_column_get_json_type; ++} SMARTCOLS_2.31; +diff --git a/libsmartcols/src/line.c b/libsmartcols/src/line.c +index debfeab78..60be2c135 100644 +--- a/libsmartcols/src/line.c ++++ b/libsmartcols/src/line.c +@@ -11,7 +11,7 @@ + /** + * SECTION: line + * @title: Line +- * @short_description: line API ++ * @short_description: cells container, also keeps tree (parent->child) information + * + * An API to access and modify per-line data and information. + */ +@@ -71,7 +71,6 @@ void scols_ref_line(struct libscols_line *ln) + */ + void scols_unref_line(struct libscols_line *ln) + { +- + if (ln && --ln->refcount <= 0) { + DBG(CELL, ul_debugobj(ln, "dealloc")); + list_del(&ln->ln_lines); +@@ -122,8 +121,6 @@ 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) +@@ -149,6 +146,35 @@ int scols_line_alloc_cells(struct libscols_line *ln, size_t n) + return 0; + } + ++int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn) ++{ ++ struct libscols_cell ce; ++ ++ if (!ln || newn >= ln->ncells || oldn >= ln->ncells) ++ return -EINVAL; ++ if (oldn == newn) ++ return 0; ++ ++ DBG(LINE, ul_debugobj(ln, "move cells[%zu] -> cells[%zu]", oldn, newn)); ++ ++ /* remember data from old position */ ++ memcpy(&ce, &ln->cells[oldn], sizeof(struct libscols_cell)); ++ ++ /* remove old position (move data behind oldn to oldn) */ ++ if (oldn + 1 < ln->ncells) ++ memmove(ln->cells + oldn, ln->cells + oldn + 1, ++ (ln->ncells - oldn - 1) * sizeof(struct libscols_cell)); ++ ++ /* create a space for new position */ ++ if (newn + 1 < ln->ncells) ++ memmove(ln->cells + newn + 1, ln->cells + newn, ++ (ln->ncells - newn - 1) * sizeof(struct libscols_cell)); ++ ++ /* copy original data to new position */ ++ memcpy(&ln->cells[newn], &ce, sizeof(struct libscols_cell)); ++ return 0; ++} ++ + /** + * scols_line_set_userdata: + * @ln: a pointer to a struct libscols_line instance +@@ -160,7 +186,6 @@ int scols_line_alloc_cells(struct libscols_line *ln, size_t n) + */ + int scols_line_set_userdata(struct libscols_line *ln, void *data) + { +- assert(ln); + if (!ln) + return -EINVAL; + ln->userdata = data; +@@ -171,12 +196,11 @@ int scols_line_set_userdata(struct libscols_line *ln, void *data) + * scols_line_get_userdata: + * @ln: a pointer to a struct libscols_line instance + * +- * Returns: 0, a negative value in case of an error. ++ * Returns: user data + */ + void *scols_line_get_userdata(struct libscols_line *ln) + { +- assert(ln); +- return ln ? ln->userdata : NULL; ++ return ln->userdata; + } + + /** +@@ -190,13 +214,10 @@ void *scols_line_get_userdata(struct libscols_line *ln) + */ + 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)); ++ DBG(LINE, ul_debugobj(ln, "remove child")); + + list_del_init(&child->ln_children); + child->parent = NULL; +@@ -217,26 +238,22 @@ int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *chil + */ + int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child) + { +- assert(ln); +- assert(child); +- + if (!ln || !child) + return -EINVAL; + ++ DBG(LINE, ul_debugobj(ln, "add child")); ++ scols_ref_line(child); ++ scols_ref_line(ln); ++ + /* 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; + } + +@@ -246,9 +263,8 @@ int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child) + * + * 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) ++struct libscols_line *scols_line_get_parent(const struct libscols_line *ln) + { +- assert(ln); + return ln ? ln->parent : NULL; + } + +@@ -260,7 +276,6 @@ struct libscols_line *scols_line_get_parent(struct libscols_line *ln) + */ + int scols_line_has_children(struct libscols_line *ln) + { +- assert(ln); + return ln ? !list_empty(&ln->ln_branch) : 0; + } + +@@ -294,29 +309,44 @@ int scols_line_next_child(struct libscols_line *ln, + return rc; + } + ++ ++/** ++ * scols_line_is_ancestor: ++ * @ln: line ++ * @parent: potential parent ++ * ++ * The function is designed to detect circular dependencies between @ln and ++ * @parent. It checks if @ln is not any (grand) parent in the @parent's tree. ++ * ++ * Since: 2.30 ++ * ++ * Returns: 0 or 1 ++ */ ++int scols_line_is_ancestor(struct libscols_line *ln, struct libscols_line *parent) ++{ ++ while (parent) { ++ if (parent == ln) ++ return 1; ++ parent = scols_line_get_parent(parent); ++ }; ++ return 0; ++} ++ + /** + * scols_line_set_color: + * @ln: a pointer to a struct libscols_line instance +- * @color: ESC sequence ++ * @color: color name or 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; ++ if (color && isalnum(*color)) { ++ color = color_sequence_from_colorname(color); ++ if (!color) ++ return -EINVAL; + } +- +- free(ln->color); +- ln->color = p; +- return 0; ++ return strdup_to_struct_member(ln, color, color); + } + + /** +@@ -325,22 +355,20 @@ int scols_line_set_color(struct libscols_line *ln, const char *color) + * + * Returns: @ln's color string, NULL in case of an error. + */ +-const char *scols_line_get_color(struct libscols_line *ln) ++const char *scols_line_get_color(const struct libscols_line *ln) + { +- assert(ln); +- return ln ? ln->color : NULL; ++ return ln->color; + } + + /** + * scols_line_get_ncells: + * @ln: a pointer to a struct libscols_line instance + * +- * Returns: @ln's number of cells ++ * Returns: number of cells + */ +-size_t scols_line_get_ncells(struct libscols_line *ln) ++size_t scols_line_get_ncells(const struct libscols_line *ln) + { +- assert(ln); +- return ln ? ln->ncells : 0; ++ return ln->ncells; + } + + /** +@@ -353,8 +381,6 @@ size_t scols_line_get_ncells(struct libscols_line *ln) + 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]; +@@ -373,15 +399,15 @@ struct libscols_cell *scols_line_get_column_cell( + struct libscols_line *ln, + struct libscols_column *cl) + { +- assert(ln); +- assert(cl); ++ if (!ln || !cl) ++ return NULL; + + return scols_line_get_cell(ln, cl->seqnum); + } + + /** + * scols_line_set_data: +- * @ln: a pointer to a struct libscols_cell instance ++ * @ln: a pointer to a struct libscols_line instance + * @n: number of the cell, whose data is to be set + * @data: actual data to set + * +@@ -396,9 +422,28 @@ int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data) + return scols_cell_set_data(ce, data); + } + ++/** ++ * scols_line_set_column_data: ++ * @ln: a pointer to a struct libscols_line instance ++ * @cl: column, whose data is to be set ++ * @data: actual data to set ++ * ++ * The same as scols_line_set_data() but cell is referenced by column object. ++ * ++ * Returns: 0, a negative value in case of an error. ++ * ++ * Since: 2.28 ++ */ ++int scols_line_set_column_data(struct libscols_line *ln, ++ struct libscols_column *cl, ++ const char *data) ++{ ++ return scols_line_set_data(ln, cl->seqnum, data); ++} ++ + /** + * scols_line_refer_data: +- * @ln: a pointer to a struct libscols_cell instance ++ * @ln: a pointer to a struct libscols_line instance + * @n: number of the cell which will refer to @data + * @data: actual data to refer to + * +@@ -413,18 +458,36 @@ int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data) + return scols_cell_refer_data(ce, data); + } + ++/** ++ * scols_line_refer_column_data: ++ * @ln: a pointer to a struct libscols_line instance ++ * @cl: column, whose data is to be set ++ * @data: actual data to refer to ++ * ++ * The same as scols_line_refer_data() but cell is referenced by column object. ++ * ++ * Returns: 0, a negative value in case of an error. ++ * ++ * Since: 2.28 ++ */ ++int scols_line_refer_column_data(struct libscols_line *ln, ++ struct libscols_column *cl, ++ char *data) ++{ ++ return scols_line_refer_data(ln, cl->seqnum, data); ++} ++ + /** + * scols_copy_line: +- * @ln: a pointer to a struct libscols_cell instance ++ * @ln: a pointer to a struct libscols_line 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 *scols_copy_line(const struct libscols_line *ln) + { + struct libscols_line *ret; + size_t i; + +- assert (ln); + if (!ln) + return NULL; + +@@ -440,7 +503,7 @@ struct libscols_line *scols_copy_line(struct libscols_line *ln) + ret->ncells = ln->ncells; + ret->seqnum = ln->seqnum; + +- DBG(LINE, ul_debugobj(ln, "copy to %p", ret)); ++ DBG(LINE, ul_debugobj(ln, "copy")); + + for (i = 0; i < ret->ncells; ++i) { + if (scols_cell_copy_content(&ret->cells[i], &ln->cells[i])) +@@ -452,5 +515,3 @@ err: + scols_unref_line(ret); + return NULL; + } +- +- +diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h +index cea4f3101..398e6f064 100644 +--- a/libsmartcols/src/smartcolsP.h ++++ b/libsmartcols/src/smartcolsP.h +@@ -13,23 +13,18 @@ + + #include "c.h" + #include "list.h" ++#include "strutils.h" + #include "colors.h" + #include "debug.h" + +-#include "libsmartcols.h" +- +-/* features */ +-#define CONFIG_LIBSMARTCOLS_ASSERT ++#include <assert.h> + +-#ifdef CONFIG_LIBSMARTCOLS_ASSERT +-# include <assert.h> +-#else +-# define assert(x) +-#endif ++#include "libsmartcols.h" + + /* + * Debug + */ ++#define SCOLS_DEBUG_HELP (1 << 0) + #define SCOLS_DEBUG_INIT (1 << 1) + #define SCOLS_DEBUG_CELL (1 << 2) + #define SCOLS_DEBUG_LINE (1 << 3) +@@ -43,7 +38,7 @@ UL_DEBUG_DECLARE_MASK(libsmartcols); + #define ON_DBG(m, x) __UL_DBG_CALL(libsmartcols, SCOLS_DEBUG_, m, x) + #define DBG_FLUSH __UL_DBG_FLUSH(libsmartcols, SCOLS_DEBUG_) + +-#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(libsmartcols) ++#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(libsmartcols) + #include "debugobj.h" + + /* +@@ -63,6 +58,8 @@ struct libscols_symbols { + char *branch; + char *vert; + char *right; ++ char *title_padding; ++ char *cell_padding; + }; + + /* +@@ -72,8 +69,10 @@ struct libscols_cell { + char *data; + char *color; + void *userdata; ++ int flags; + }; + ++extern int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn); + + /* + * Table column +@@ -86,19 +85,36 @@ struct libscols_column { + 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 */ ++ size_t width_treeart; /* size of the tree ascii art */ + double width_hint; /* hint (N < 1 is in percent of termwidth) */ + ++ int json_type; /* SCOLS_JSON_* */ ++ + int flags; + int is_extreme; + char *color; /* default column color */ ++ char *safechars; /* do not encode this bytes */ ++ ++ char *pending_data; ++ size_t pending_data_sz; ++ char *pending_data_buf; + + int (*cmpfunc)(struct libscols_cell *, + struct libscols_cell *, + void *); /* cells comparison function */ + void *cmpfunc_data; + ++ size_t (*wrap_chunksize)(const struct libscols_column *, ++ const char *, void *); ++ char *(*wrap_nextchunk)(const struct libscols_column *, ++ char *, void *); ++ void *wrapfunc_data; ++ ++ + struct libscols_cell header; + struct list_head cl_columns; ++ ++ struct libscols_table *table; + }; + + /* +@@ -124,7 +140,8 @@ struct libscols_line { + enum { + SCOLS_FMT_HUMAN = 0, /* default, human readable */ + SCOLS_FMT_RAW, /* space separated */ +- SCOLS_FMT_EXPORT /* COLNAME="data" ... */ ++ SCOLS_FMT_EXPORT, /* COLNAME="data" ... */ ++ SCOLS_FMT_JSON /* http://en.wikipedia.org/wiki/JSON */ + }; + + /* +@@ -132,11 +149,14 @@ enum { + */ + struct libscols_table { + int refcount; ++ char *name; /* optional table name (for JSON) */ + 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 termwidth; /* terminal width (number of columns) */ ++ size_t termheight; /* terminal height (number of lines) */ + size_t termreduce; /* extra blank space */ ++ int termforce; /* SCOLS_TERMFORCE_* */ + FILE *out; /* output stream */ + + char *colsep; /* column separator */ +@@ -145,15 +165,28 @@ struct libscols_table { + struct list_head tb_columns; + struct list_head tb_lines; + struct libscols_symbols *symbols; ++ struct libscols_cell title; /* optional table title (for humans) */ + ++ int indent; /* indention counter */ ++ int indent_last_sep;/* last printed has been line separator */ + int format; /* SCOLS_FMT_* */ + ++ size_t termlines_used; /* printed line counter */ ++ size_t header_next; /* where repeat header */ ++ + /* 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 */ ++ padding_debug :1, /* output visible padding chars */ ++ maxout :1, /* maximize output */ ++ header_repeat :1, /* print header after libscols_table->termheight */ ++ header_printed :1, /* header already printed */ ++ priv_symbols :1, /* default private symbols */ ++ no_headings :1, /* don't print header */ ++ no_encode :1, /* don't care about control and non-printable chars */ ++ no_linesep :1, /* don't print line separator */ ++ no_wrap :1; /* never wrap lines */ + }; + + #define IS_ITER_FORWARD(_i) ((_i)->direction == SCOLS_ITER_FORWARD) +@@ -173,4 +206,13 @@ struct libscols_table { + (itr)->p->next : (itr)->p->prev; \ + } while(0) + ++ ++static inline int scols_iter_is_last(const struct libscols_iter *itr) ++{ ++ if (!itr || !itr->head || !itr->p) ++ return 0; ++ ++ return itr->p == itr->head; ++} ++ + #endif /* _LIBSMARTCOLS_PRIVATE_H */ +diff --git a/libsmartcols/src/symbols.c b/libsmartcols/src/symbols.c +index 2b8f81dc9..6ddf1869b 100644 +--- a/libsmartcols/src/symbols.c ++++ b/libsmartcols/src/symbols.c +@@ -2,6 +2,7 @@ + * symbols.c - routines for symbol handling + * + * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com> ++ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. +@@ -10,7 +11,7 @@ + /** + * SECTION: symbols + * @title: Symbols +- * @short_description: symbols API ++ * @short_description: allows to overwrite default output chars (for ascii art) + * + * An API to access and modify data and information per symbol/symbol group. + */ +@@ -61,115 +62,112 @@ void scols_unref_symbols(struct libscols_symbols *sy) + free(sy->branch); + free(sy->vert); + free(sy->right); ++ free(sy->title_padding); ++ free(sy->cell_padding); + free(sy); + } + } + + /** + * scols_symbols_set_branch: +- * @sb: a pointer to a struct libscols_symbols instance ++ * @sy: 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) ++int scols_symbols_set_branch(struct libscols_symbols *sy, 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; ++ return strdup_to_struct_member(sy, branch, str); + } + + /** + * scols_symbols_set_vertical: +- * @sb: a pointer to a struct libscols_symbols instance ++ * @sy: 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) ++int scols_symbols_set_vertical(struct libscols_symbols *sy, 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; ++ return strdup_to_struct_member(sy, vert, str); + } + + /** + * scols_symbols_set_right: +- * @sb: a pointer to a struct libscols_symbols instance ++ * @sy: 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) ++int scols_symbols_set_right(struct libscols_symbols *sy, const char *str) + { +- char *p = NULL; ++ return strdup_to_struct_member(sy, right, str); ++} + +- assert(sb); ++/** ++ * scols_symbols_set_title_padding: ++ * @sy: a pointer to a struct libscols_symbols instance ++ * @str: a string which will represent the symbols which fill title output ++ * ++ * The current implementation uses only the first byte from the padding string. ++ * A multibyte chars are not supported yet. ++ * ++ * Returns: 0, a negative value in case of an error. ++ * ++ * Since: 2.28 ++ */ ++int scols_symbols_set_title_padding(struct libscols_symbols *sy, const char *str) ++{ ++ return strdup_to_struct_member(sy, title_padding, str); ++} + +- if (!sb) +- return -EINVAL; +- if (str) { +- p = strdup(str); +- if (!p) +- return -ENOMEM; +- } +- free(sb->right); +- sb->right = p; +- return 0; ++/** ++ * scols_symbols_set_cell_padding: ++ * @sy: a pointer to a struct libscols_symbols instance ++ * @str: a string which will represent the symbols which fill cells ++ * ++ * The padding char has to take up just one cell on the terminal. ++ * ++ * Returns: 0, a negative value in case of an error. ++ * ++ * Since: 2.29 ++ */ ++int scols_symbols_set_cell_padding(struct libscols_symbols *sy, const char *str) ++{ ++ return strdup_to_struct_member(sy, cell_padding, str); + } + + /** + * scols_copy_symbols: +- * @sb: a pointer to a struct libscols_symbols instance ++ * @sy: 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. ++ * Returns: a newly allocated copy of the @sy symbol group or NULL in case of an error. + */ +-struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sb) ++struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sy) + { + struct libscols_symbols *ret; + int rc; + +- assert(sb); +- if (!sb) ++ assert(sy); ++ if (!sy) + return NULL; + + ret = scols_new_symbols(); + if (!ret) + return NULL; + +- rc = scols_symbols_set_branch(ret, sb->branch); ++ rc = scols_symbols_set_branch(ret, sy->branch); ++ if (!rc) ++ rc = scols_symbols_set_vertical(ret, sy->vert); + if (!rc) +- rc = scols_symbols_set_vertical(ret, sb->vert); ++ rc = scols_symbols_set_right(ret, sy->right); + if (!rc) +- rc = scols_symbols_set_right(ret, sb->right); ++ rc = scols_symbols_set_title_padding(ret, sy->title_padding); ++ if (!rc) ++ rc = scols_symbols_set_cell_padding(ret, sy->cell_padding); + if (!rc) + return ret; + + scols_unref_symbols(ret); + return NULL; +- + } +- +- +diff --git a/libsmartcols/src/table.c b/libsmartcols/src/table.c +index 8c404f858..979a09a39 100644 +--- a/libsmartcols/src/table.c ++++ b/libsmartcols/src/table.c +@@ -3,6 +3,7 @@ + * + * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com> + * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com> ++ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. +@@ -11,7 +12,7 @@ + /** + * SECTION: table + * @title: Table +- * @short_description: table data API ++ * @short_description: container for rows and columns + * + * Table data manipulation API. + */ +@@ -24,7 +25,7 @@ + #include <ctype.h> + + #include "nls.h" +-#include "widechar.h" ++#include "ttyutils.h" + #include "smartcolsP.h" + + #ifdef HAVE_WIDECHAR +@@ -38,6 +39,20 @@ + list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns) + + ++static void check_padding_debug(struct libscols_table *tb) ++{ ++ const char *str; ++ ++ assert(libsmartcols_debug_mask); /* debug has to be enabled! */ ++ ++ str = getenv("LIBSMARTCOLS_DEBUG_PADDING"); ++ if (!str || (strcmp(str, "on") != 0 && strcmp(str, "1") != 0)) ++ return; ++ ++ DBG(INIT, ul_debugobj(tb, "padding debug: ENABLE")); ++ tb->padding_debug = 1; ++} ++ + /** + * scols_new_table: + * +@@ -46,6 +61,7 @@ + struct libscols_table *scols_new_table(void) + { + struct libscols_table *tb; ++ int c, l; + + tb = calloc(1, sizeof(struct libscols_table)); + if (!tb) +@@ -54,10 +70,16 @@ struct libscols_table *scols_new_table(void) + tb->refcount = 1; + tb->out = stdout; + ++ get_terminal_dimension(&c, &l); ++ tb->termwidth = c > 0 ? c : 80; ++ tb->termheight = l > 0 ? l : 24; ++ + INIT_LIST_HEAD(&tb->tb_lines); + INIT_LIST_HEAD(&tb->tb_columns); + + DBG(TAB, ul_debugobj(tb, "alloc")); ++ ON_DBG(INIT, check_padding_debug(tb)); ++ + return tb; + } + +@@ -87,44 +109,102 @@ void scols_unref_table(struct libscols_table *tb) + scols_table_remove_lines(tb); + scols_table_remove_columns(tb); + scols_unref_symbols(tb->symbols); ++ scols_reset_cell(&tb->title); + free(tb->linesep); + free(tb->colsep); ++ free(tb->name); + free(tb); + } + } + ++/** ++ * scols_table_set_name: ++ * @tb: a pointer to a struct libscols_table instance ++ * @name: a name ++ * ++ * The table name is used for example for JSON top level object name. ++ * ++ * Returns: 0, a negative number in case of an error. ++ * ++ * Since: 2.27 ++ */ ++int scols_table_set_name(struct libscols_table *tb, const char *name) ++{ ++ return strdup_to_struct_member(tb, name, name); ++} ++ ++/** ++ * scols_table_get_name: ++ * @tb: a pointer to a struct libscols_table instance ++ * ++ * Returns: The current name setting of the table @tb ++ * ++ * Since: 2.29 ++ */ ++const char *scols_table_get_name(const struct libscols_table *tb) ++{ ++ return tb->name; ++} ++ ++/** ++ * scols_table_get_title: ++ * @tb: a pointer to a struct libscols_table instance ++ * ++ * The returned pointer is possible to modify by cell functions. Note that ++ * title output alignment on non-tty is hardcoded to 80 output chars. For the ++ * regular terminal it's based on terminal width. ++ * ++ * Returns: Title of the table, or NULL in case of blank title. ++ * ++ * Since: 2.28 ++ */ ++struct libscols_cell *scols_table_get_title(struct libscols_table *tb) ++{ ++ return &tb->title; ++} ++ + /** + * 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. ++ * Adds @cl to @tb's column list. The column cannot be shared between more ++ * tables. + * + * 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); ++ struct libscols_iter itr; ++ struct libscols_line *ln; ++ int rc = 0; + +- if (!tb || !cl || !list_empty(&tb->tb_lines)) ++ if (!tb || !cl || cl->table) + return -EINVAL; + + if (cl->flags & SCOLS_FL_TREE) + tb->ntreecols++; + +- DBG(TAB, ul_debugobj(tb, "add column %p", cl)); ++ DBG(TAB, ul_debugobj(tb, "add column")); + list_add_tail(&cl->cl_columns, &tb->tb_columns); + cl->seqnum = tb->ncols++; ++ cl->table = tb; + 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. ++ if (list_empty(&tb->tb_lines)) ++ return 0; ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ ++ /* Realloc line cell arrays + */ +- return 0; ++ while (scols_table_next_line(tb, &itr, &ln) == 0) { ++ rc = scols_line_alloc_cells(ln, tb->ncols); ++ if (rc) ++ break; ++ } ++ ++ return rc; + } + + /** +@@ -139,18 +219,16 @@ int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl + 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)); ++ DBG(TAB, ul_debugobj(tb, "remove column")); + list_del_init(&cl->cl_columns); + tb->ncols--; ++ cl->table = NULL; + scols_unref_column(cl); + return 0; + } +@@ -165,8 +243,6 @@ int scols_table_remove_column(struct libscols_table *tb, + */ + int scols_table_remove_columns(struct libscols_table *tb) + { +- assert(tb); +- + if (!tb || !list_empty(&tb->tb_lines)) + return -EINVAL; + +@@ -179,12 +255,64 @@ int scols_table_remove_columns(struct libscols_table *tb) + return 0; + } + ++/** ++ * scols_table_move_column: ++ * @tb: table ++ * @pre: column before the column ++ * @cl: column to move ++ * ++ * Move the @cl behind @pre. If the @pre is NULL then the @col is the first ++ * column in the table. ++ * ++ * Since: 2.30 ++ * ++ * Returns: 0, a negative number in case of an error. ++ */ ++int scols_table_move_column(struct libscols_table *tb, ++ struct libscols_column *pre, ++ struct libscols_column *cl) ++{ ++ struct list_head *head; ++ struct libscols_iter itr; ++ struct libscols_column *p; ++ struct libscols_line *ln; ++ size_t n = 0, oldseq; ++ ++ if (!tb || !cl) ++ return -EINVAL; ++ ++ if (pre && pre->seqnum + 1 == cl->seqnum) ++ return 0; ++ if (pre == NULL && cl->seqnum == 0) ++ return 0; ++ ++ DBG(TAB, ul_debugobj(tb, "move column %zu behind %zu", ++ cl->seqnum, pre? pre->seqnum : 0)); ++ ++ list_del_init(&cl->cl_columns); /* remove from old position */ ++ ++ head = pre ? &pre->cl_columns : &tb->tb_columns; ++ list_add(&cl->cl_columns, head); /* add to the new place */ ++ ++ oldseq = cl->seqnum; ++ ++ /* fix seq. numbers */ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_column(tb, &itr, &p) == 0) ++ p->seqnum = n++; ++ ++ /* move data in lines */ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_line(tb, &itr, &ln) == 0) ++ scols_line_move_cells(ln, cl->seqnum, oldseq); ++ return 0; ++} + + /** + * scols_table_new_column: + * @tb: table + * @name: column header +- * @whint: column width hint (absolute width: N > 1; relative width: N < 1) ++ * @whint: column width hint (absolute width: N > 1; relative width: 0 < N < 1) + * @flags: flags integer + * + * This is shortcut for +@@ -193,17 +321,36 @@ int scols_table_remove_columns(struct libscols_table *tb) + * scols_column_set_....(cl, ...); + * scols_table_add_column(tb, cl); + * +- * The column width is possible to define by three ways: ++ * The column width is possible to define by: ++ * ++ * @whint: 0 < N < 1 : relative width, percent of terminal width ++ * ++ * @whint: N >= 1 : absolute width, empty column will be truncated to ++ * the column header width if no specified STRICTWIDTH flag + * +- * @whint = 0..1 : relative width, percent of terminal width ++ * Note that if table has disabled "maxout" flag (disabled by default) than ++ * relative width is used as a hint only. It's possible that column will be ++ * narrow if the specified size is too large for column data. + * +- * @whint = 1..N : absolute width, empty colum will be truncated to +- * the column header width + * +- * @whint = 1..N ++ * If the width of all columns is greater than terminal width then library ++ * tries to reduce width of the individual columns. It's done in three stages: + * +- * The column is necessary to address by +- * sequential number. The first defined column has the colnum = 0. For example: ++ * #1 reduce columns with SCOLS_FL_TRUNC flag and with relative width if the ++ * width is greater than width defined by @whint (@whint * terminal_width) ++ * ++ * #2 reduce all columns with SCOLS_FL_TRUNC flag ++ * ++ * #3 reduce all columns with relative width ++ * ++ * The next stage is always used if the previous stage is unsuccessful. Note ++ * that SCOLS_FL_WRAP is interpreted as SCOLS_FL_TRUNC when calculate column ++ * width (if custom wrap function is not specified), but the final text is not ++ * truncated, but wrapped to multi-line cell. ++ * ++ * ++ * 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 +@@ -222,7 +369,6 @@ struct libscols_column *scols_table_new_column(struct libscols_table *tb, + struct libscols_column *cl; + struct libscols_cell *hr; + +- assert (tb); + if (!tb) + return NULL; + +@@ -282,29 +428,26 @@ int scols_table_next_column(struct libscols_table *tb, + return rc; + } + +- + /** + * scols_table_get_ncols: + * @tb: table + * +- * Returns: the ncols table member, a negative number in case of an error. ++ * Returns: the ncols table member. + */ +-int scols_table_get_ncols(struct libscols_table *tb) ++size_t scols_table_get_ncols(const struct libscols_table *tb) + { +- assert(tb); +- return tb ? tb->ncols : -EINVAL; ++ return tb->ncols; + } + + /** + * scols_table_get_nlines: + * @tb: table + * +- * Returns: the nlines table member, a negative number in case of an error. ++ * Returns: the nlines table member. + */ +-int scols_table_get_nlines(struct libscols_table *tb) ++size_t scols_table_get_nlines(const struct libscols_table *tb) + { +- assert(tb); +- return tb ? tb->nlines : -EINVAL; ++ return tb->nlines; + } + + /** +@@ -335,10 +478,9 @@ int scols_table_set_stream(struct libscols_table *tb, FILE *stream) + * + * Returns: stream pointer, NULL in case of an error or an unset stream. + */ +-FILE *scols_table_get_stream(struct libscols_table *tb) ++FILE *scols_table_get_stream(const struct libscols_table *tb) + { +- assert(tb); +- return tb ? tb->out: NULL; ++ return tb->out; + } + + /** +@@ -346,13 +488,20 @@ FILE *scols_table_get_stream(struct libscols_table *tb) + * @tb: table + * @reduce: width + * +- * Reduce the output width to @reduce. ++ * If necessary then libsmartcols use all terminal width, the @reduce setting ++ * provides extra space (for example for borders in ncurses applications). ++ * ++ * The @reduce must be smaller than terminal width, otherwise it's silently ++ * ignored. The reduction is not applied when STDOUT_FILENO is not terminal. ++ * ++ * Note that after output initialization (scols_table_print_* calls) the width ++ * will be reduced, this behavior affects subsequenced scols_table_get_termwidth() ++ * calls. + * + * 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; + +@@ -374,7 +523,6 @@ struct libscols_column *scols_table_get_column(struct libscols_table *tb, + struct libscols_iter itr; + struct libscols_column *cl; + +- assert(tb); + if (!tb) + return NULL; + if (n >= tb->ncols) +@@ -400,11 +548,7 @@ struct libscols_column *scols_table_get_column(struct libscols_table *tb, + */ + int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln) + { +- +- assert(tb); +- assert(ln); +- +- if (!tb || !ln) ++ if (!tb || !ln || tb->ncols == 0) + return -EINVAL; + + if (tb->ncols > ln->ncells) { +@@ -413,7 +557,7 @@ int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln) + return rc; + } + +- DBG(TAB, ul_debugobj(tb, "add line %p", ln)); ++ DBG(TAB, ul_debugobj(tb, "add line")); + list_add_tail(&ln->ln_lines, &tb->tb_lines); + ln->seqnum = tb->nlines++; + scols_ref_line(ln); +@@ -433,13 +577,10 @@ int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln) + 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)); ++ DBG(TAB, ul_debugobj(tb, "remove line")); + list_del_init(&ln->ln_lines); + tb->nlines--; + scols_unref_line(ln); +@@ -454,7 +595,6 @@ int scols_table_remove_line(struct libscols_table *tb, + */ + void scols_table_remove_lines(struct libscols_table *tb) + { +- assert(tb); + if (!tb) + return; + +@@ -517,9 +657,6 @@ struct libscols_line *scols_table_new_line(struct libscols_table *tb, + { + struct libscols_line *ln; + +- assert(tb); +- assert(tb->ncols); +- + if (!tb || !tb->ncols) + return NULL; + +@@ -544,13 +681,7 @@ err: + * @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 ++ * Returns: a line or NULL + */ + struct libscols_line *scols_table_get_line(struct libscols_table *tb, + size_t n) +@@ -558,7 +689,6 @@ struct libscols_line *scols_table_get_line(struct libscols_table *tb, + struct libscols_iter itr; + struct libscols_line *ln; + +- assert(tb); + if (!tb) + return NULL; + if (n >= tb->nlines) +@@ -588,14 +718,13 @@ struct libscols_table *scols_copy_table(struct libscols_table *tb) + 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)); ++ DBG(TAB, ul_debugobj(tb, "copy")); + + if (tb->symbols) + scols_table_set_symbols(ret, tb->symbols); +@@ -639,54 +768,141 @@ err: + return NULL; + } + ++/** ++ * scols_table_set_default_symbols: ++ * @tb: table ++ * ++ * The library check the current environment to select ASCII or UTF8 symbols. ++ * This default behavior could be controlled by scols_table_enable_ascii(). ++ * ++ * Use scols_table_set_symbols() to unset symbols or use your own setting. ++ * ++ * Returns: 0, a negative value in case of an error. ++ * ++ * Since: 2.29 ++ */ ++int scols_table_set_default_symbols(struct libscols_table *tb) ++{ ++ struct libscols_symbols *sy; ++ int rc; ++ ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "setting default symbols")); ++ ++ sy = scols_new_symbols(); ++ if (!sy) ++ return -ENOMEM; ++ ++#if defined(HAVE_WIDECHAR) ++ if (!scols_table_is_ascii(tb) && ++ !strcmp(nl_langinfo(CODESET), "UTF-8")) { ++ scols_symbols_set_branch(sy, UTF_VR UTF_H); ++ scols_symbols_set_vertical(sy, UTF_V " "); ++ scols_symbols_set_right(sy, UTF_UR UTF_H); ++ } else ++#endif ++ { ++ scols_symbols_set_branch(sy, "|-"); ++ scols_symbols_set_vertical(sy, "| "); ++ scols_symbols_set_right(sy, "`-"); ++ } ++ scols_symbols_set_title_padding(sy, " "); ++ scols_symbols_set_cell_padding(sy, " "); ++ ++ rc = scols_table_set_symbols(tb, sy); ++ scols_unref_symbols(sy); ++ return rc; ++} ++ ++ + /** + * 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(). ++ * draw tree output. If no symbols are used for the table then library creates ++ * default temporary symbols to draw output by scols_table_set_default_symbols(). ++ * ++ * If @sy is NULL then remove reference from the currently used symbols. + * + * 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 */ ++ /* remove old */ ++ if (tb->symbols) { ++ DBG(TAB, ul_debugobj(tb, "remove symbols reference")); + scols_unref_symbols(tb->symbols); ++ tb->symbols = NULL; ++ } ++ ++ /* set new */ + if (sy) { /* ref user defined */ ++ DBG(TAB, ul_debugobj(tb, "set symbols")); + 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_get_symbols: ++ * @tb: table ++ * ++ * Returns: pointer to symbols table. ++ * ++ * Since: 2.29 ++ */ ++struct libscols_symbols *scols_table_get_symbols(const struct libscols_table *tb) ++{ ++ return tb->symbols; ++} ++ ++/** ++ * scols_table_enable_nolinesep: ++ * @tb: table ++ * @enable: 1 or 0 ++ * ++ * Enable/disable line separator printing. This is useful if you want to ++ * re-printing the same line more than once (e.g. progress bar). Don't use it ++ * if you're not sure. ++ * ++ * Note that for the last line in the table the separator is disabled at all. ++ * The library differentiate between table terminator and line terminator ++ * (although for standard output \n byte is used in both cases). ++ * ++ * Returns: 0 on success, negative number in case of an error. ++ */ ++int scols_table_enable_nolinesep(struct libscols_table *tb, int enable) ++{ ++ if (!tb) ++ return -EINVAL; + ++ DBG(TAB, ul_debugobj(tb, "nolinesep: %s", enable ? "ENABLE" : "DISABLE")); ++ tb->no_linesep = enable ? 1 : 0; + return 0; + } ++ ++/** ++ * scols_table_is_nolinesep: ++ * @tb: a pointer to a struct libscols_table instance ++ * ++ * Returns: 1 if line separator printing is disabled. ++ * ++ * Since: 2.29 ++ */ ++int scols_table_is_nolinesep(const struct libscols_table *tb) ++{ ++ return tb->no_linesep; ++} ++ + /** + * scols_table_enable_colors: + * @tb: table +@@ -698,7 +914,6 @@ int scols_table_set_symbols(struct libscols_table *tb, + */ + int scols_table_enable_colors(struct libscols_table *tb, int enable) + { +- assert(tb); + if (!tb) + return -EINVAL; + +@@ -706,19 +921,19 @@ int scols_table_enable_colors(struct libscols_table *tb, int enable) + 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. ++ * (export, raw, JSON, ...) 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; + +@@ -730,6 +945,31 @@ int scols_table_enable_raw(struct libscols_table *tb, int enable) + return 0; + } + ++/** ++ * scols_table_enable_json: ++ * @tb: table ++ * @enable: 1 or 0 ++ * ++ * Enable/disable JSON output format. The parsable output formats ++ * (export, raw, JSON, ...) are mutually exclusive. ++ * ++ * Returns: 0 on success, negative number in case of an error. ++ * ++ * Since: 2.27 ++ */ ++int scols_table_enable_json(struct libscols_table *tb, int enable) ++{ ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "json: %s", enable ? "ENABLE" : "DISABLE")); ++ if (enable) ++ tb->format = SCOLS_FMT_JSON; ++ else if (tb->format == SCOLS_FMT_JSON) ++ tb->format = 0; ++ return 0; ++} ++ + /** + * scols_table_enable_export: + * @tb: table +@@ -742,7 +982,6 @@ int scols_table_enable_raw(struct libscols_table *tb, int enable) + */ + int scols_table_enable_export(struct libscols_table *tb, int enable) + { +- assert(tb); + if (!tb) + return -EINVAL; + +@@ -771,7 +1010,6 @@ int scols_table_enable_export(struct libscols_table *tb, int enable) + */ + int scols_table_enable_ascii(struct libscols_table *tb, int enable) + { +- assert(tb); + if (!tb) + return -EINVAL; + +@@ -791,7 +1029,6 @@ int scols_table_enable_ascii(struct libscols_table *tb, int enable) + */ + 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")); +@@ -799,6 +1036,28 @@ int scols_table_enable_noheadings(struct libscols_table *tb, int enable) + return 0; + } + ++/** ++ * scols_table_enable_header_repeat: ++ * @tb: table ++ * @enable: 1 or 0 ++ * ++ * Enable/disable header line repeat. The header line is printed only once by ++ * default. Note that the flag will be silently ignored and disabled if the ++ * output is not on terminal or output format is JSON, raw, etc. ++ * ++ * Returns: 0 on success, negative number in case of an error. ++ * ++ * Since: 2.31 ++ */ ++int scols_table_enable_header_repeat(struct libscols_table *tb, int enable) ++{ ++ if (!tb) ++ return -EINVAL; ++ DBG(TAB, ul_debugobj(tb, "header-repeat: %s", enable ? "ENABLE" : "DISABLE")); ++ tb->header_repeat = enable ? 1 : 0; ++ return 0; ++} ++ + /** + * scols_table_enable_maxout: + * @tb: table +@@ -811,7 +1070,6 @@ int scols_table_enable_noheadings(struct libscols_table *tb, int enable) + */ + 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")); +@@ -819,28 +1077,92 @@ int scols_table_enable_maxout(struct libscols_table *tb, int enable) + return 0; + } + ++/** ++ * scols_table_enable_nowrap: ++ * @tb: table ++ * @enable: 1 or 0 ++ * ++ * Never continue on next line, remove last column(s) when too large, truncate last column. ++ * ++ * Returns: 0 on success, negative number in case of an error. ++ * ++ * Since: 2.28 ++ */ ++int scols_table_enable_nowrap(struct libscols_table *tb, int enable) ++{ ++ if (!tb) ++ return -EINVAL; ++ DBG(TAB, ul_debugobj(tb, "nowrap: %s", enable ? "ENABLE" : "DISABLE")); ++ tb->no_wrap = enable ? 1 : 0; ++ return 0; ++} ++ ++/** ++ * scols_table_is_nowrap: ++ * @tb: a pointer to a struct libscols_table instance ++ * ++ * Returns: 1 if nowrap is enabled. ++ * ++ * Since: 2.29 ++ */ ++int scols_table_is_nowrap(const struct libscols_table *tb) ++{ ++ return tb->no_wrap; ++} ++ ++/** ++ * scols_table_enable_noencoding: ++ * @tb: table ++ * @enable: 1 or 0 ++ * ++ * The library encode non-printable and control chars by \xHEX by default. ++ * ++ * Returns: 0 on success, negative number in case of an error. ++ * ++ * Since: 2.31 ++ */ ++int scols_table_enable_noencoding(struct libscols_table *tb, int enable) ++{ ++ if (!tb) ++ return -EINVAL; ++ DBG(TAB, ul_debugobj(tb, "encoding: %s", enable ? "ENABLE" : "DISABLE")); ++ tb->no_encode = enable ? 1 : 0; ++ return 0; ++} ++ ++/** ++ * scols_table_is_noencoding: ++ * @tb: a pointer to a struct libscols_table instance ++ * ++ * Returns: 1 if encoding is disabled. ++ * ++ * Since: 2.31 ++ */ ++int scols_table_is_noencoding(const struct libscols_table *tb) ++{ ++ return tb->no_encode; ++} ++ + /** + * scols_table_colors_wanted: + * @tb: table + * + * Returns: 1 if colors are enabled. + */ +-int scols_table_colors_wanted(struct libscols_table *tb) ++int scols_table_colors_wanted(const struct libscols_table *tb) + { +- assert(tb); +- return tb && tb->colors_wanted; ++ return tb->colors_wanted; + } + + /** + * scols_table_is_empty: + * @tb: table + * +- * Returns: 1 if the table is empty. ++ * Returns: 1 if the table is empty. + */ +-int scols_table_is_empty(struct libscols_table *tb) ++int scols_table_is_empty(const struct libscols_table *tb) + { +- assert(tb); +- return !tb || !tb->nlines; ++ return !tb->nlines; + } + + /** +@@ -849,10 +1171,9 @@ int scols_table_is_empty(struct libscols_table *tb) + * + * Returns: 1 if ASCII tree is enabled. + */ +-int scols_table_is_ascii(struct libscols_table *tb) ++int scols_table_is_ascii(const struct libscols_table *tb) + { +- assert(tb); +- return tb && tb->ascii; ++ return tb->ascii; + } + + /** +@@ -861,10 +1182,22 @@ int scols_table_is_ascii(struct libscols_table *tb) + * + * Returns: 1 if header output is disabled. + */ +-int scols_table_is_noheadings(struct libscols_table *tb) ++int scols_table_is_noheadings(const struct libscols_table *tb) + { +- assert(tb); +- return tb && tb->no_headings; ++ return tb->no_headings; ++} ++ ++/** ++ * scols_table_is_header_repeat ++ * @tb: table ++ * ++ * Returns: 1 if header repeat is enabled. ++ * ++ * Since: 2.31 ++ */ ++int scols_table_is_header_repeat(const struct libscols_table *tb) ++{ ++ return tb->header_repeat; + } + + /** +@@ -873,10 +1206,9 @@ int scols_table_is_noheadings(struct libscols_table *tb) + * + * Returns: 1 if export output format is enabled. + */ +-int scols_table_is_export(struct libscols_table *tb) ++int scols_table_is_export(const struct libscols_table *tb) + { +- assert(tb); +- return tb && tb->format == SCOLS_FMT_EXPORT; ++ return tb->format == SCOLS_FMT_EXPORT; + } + + /** +@@ -885,23 +1217,33 @@ int scols_table_is_export(struct libscols_table *tb) + * + * Returns: 1 if raw output format is enabled. + */ +-int scols_table_is_raw(struct libscols_table *tb) ++int scols_table_is_raw(const struct libscols_table *tb) + { +- assert(tb); +- return tb && tb->format == SCOLS_FMT_RAW; ++ return tb->format == SCOLS_FMT_RAW; + } + ++/** ++ * scols_table_is_json: ++ * @tb: table ++ * ++ * Returns: 1 if JSON output format is enabled. ++ * ++ * Since: 2.27 ++ */ ++int scols_table_is_json(const struct libscols_table *tb) ++{ ++ return tb->format == SCOLS_FMT_JSON; ++} + + /** + * scols_table_is_maxout + * @tb: table + * +- * Returns: 1 if output maximization is enabled, negative value in case of an error. ++ * Returns: 1 if output maximization is enabled or 0 + */ +-int scols_table_is_maxout(struct libscols_table *tb) ++int scols_table_is_maxout(const struct libscols_table *tb) + { +- assert(tb); +- return tb && tb->maxout; ++ return tb->maxout; + } + + /** +@@ -910,10 +1252,9 @@ int scols_table_is_maxout(struct libscols_table *tb) + * + * Returns: returns 1 tree-like output is expected. + */ +-int scols_table_is_tree(struct libscols_table *tb) ++int scols_table_is_tree(const struct libscols_table *tb) + { +- assert(tb); +- return tb && tb->ntreecols > 0; ++ return tb->ntreecols > 0; + } + + /** +@@ -922,29 +1263,12 @@ int scols_table_is_tree(struct libscols_table *tb) + * @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; ++ return strdup_to_struct_member(tb, colsep, sep); + } + + /** +@@ -958,23 +1282,7 @@ int scols_table_set_column_separator(struct libscols_table *tb, const char *sep) + */ + 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; ++ return strdup_to_struct_member(tb, linesep, sep); + } + + /** +@@ -983,12 +1291,8 @@ int scols_table_set_line_separator(struct libscols_table *tb, const char *sep) + * + * Returns: @tb column separator, NULL in case of an error + */ +-char *scols_table_get_column_separator(struct libscols_table *tb) ++const char *scols_table_get_column_separator(const struct libscols_table *tb) + { +- assert (tb); +- +- if (!tb) +- return NULL; + return tb->colsep; + } + +@@ -998,17 +1302,12 @@ char *scols_table_get_column_separator(struct libscols_table *tb) + * + * Returns: @tb line separator, NULL in case of an error + */ +-char *scols_table_get_line_separator(struct libscols_table *tb) ++const char *scols_table_get_line_separator(const 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) ++/* for lines in the struct libscols_line->ln_lines list */ ++static int cells_cmp_wrapper_lines(struct list_head *a, struct list_head *b, void *data) + { + struct libscols_column *cl = (struct libscols_column *) data; + struct libscols_line *ra, *rb; +@@ -1026,24 +1325,218 @@ static int cells_cmp_wrapper(struct list_head *a, struct list_head *b, void *dat + return cl->cmpfunc(ca, cb, cl->cmpfunc_data); + } + ++/* for lines in the struct libscols_line->ln_children list */ ++static int cells_cmp_wrapper_children(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_children); ++ rb = list_entry(b, struct libscols_line, ln_children); ++ ca = scols_line_get_cell(ra, cl->seqnum); ++ cb = scols_line_get_cell(rb, cl->seqnum); ++ ++ return cl->cmpfunc(ca, cb, cl->cmpfunc_data); ++} ++ ++ ++static int sort_line_children(struct libscols_line *ln, struct libscols_column *cl) ++{ ++ struct list_head *p; ++ ++ if (list_empty(&ln->ln_branch)) ++ return 0; ++ ++ list_for_each(p, &ln->ln_branch) { ++ struct libscols_line *chld = ++ list_entry(p, struct libscols_line, ln_children); ++ sort_line_children(chld, cl); ++ } ++ ++ list_sort(&ln->ln_branch, cells_cmp_wrapper_children, cl); ++ return 0; ++} ++ + /** + * scols_sort_table: + * @tb: table + * @cl: order by this column + * +- * Orders the table by the column. See also scols_column_set_cmpfunc(). ++ * Orders the table by the column. See also scols_column_set_cmpfunc(). If the ++ * tree output is enabled then children in the tree are recursively sorted too. + * + * 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) ++ if (!tb || !cl || !cl->cmpfunc) + return -EINVAL; + + DBG(TAB, ul_debugobj(tb, "sorting table")); +- list_sort(&tb->tb_lines, cells_cmp_wrapper, cl); ++ list_sort(&tb->tb_lines, cells_cmp_wrapper_lines, cl); ++ ++ if (scols_table_is_tree(tb)) { ++ struct libscols_line *ln; ++ struct libscols_iter itr; ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_line(tb, &itr, &ln) == 0) ++ sort_line_children(ln, cl); ++ } ++ ++ return 0; ++} ++ ++static struct libscols_line *move_line_and_children(struct libscols_line *ln, struct libscols_line *pre) ++{ ++ if (pre) { ++ list_del_init(&ln->ln_lines); /* remove from old position */ ++ list_add(&ln->ln_lines, &pre->ln_lines); /* add to the new place (behind @pre) */ ++ } ++ pre = ln; ++ ++ if (!list_empty(&ln->ln_branch)) { ++ struct list_head *p; ++ ++ list_for_each(p, &ln->ln_branch) { ++ struct libscols_line *chld = ++ list_entry(p, struct libscols_line, ln_children); ++ pre = move_line_and_children(chld, pre); ++ } ++ } ++ ++ return pre; ++} ++ ++/** ++ * scols_sort_table_by_tree: ++ * @tb: table ++ * ++ * Reorders lines in the table by parent->child relation. Note that order of ++ * the lines in the table is independent on the tree hierarchy. ++ * ++ * Since: 2.30 ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_sort_table_by_tree(struct libscols_table *tb) ++{ ++ struct libscols_line *ln; ++ struct libscols_iter itr; ++ ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "sorting table by tree")); ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_line(tb, &itr, &ln) == 0) { ++ if (ln->parent) ++ continue; ++ ++ move_line_and_children(ln, NULL); ++ } ++ ++ return 0; ++} ++ ++ ++/** ++ * scols_table_set_termforce: ++ * @tb: table ++ * @force: SCOLS_TERMFORCE_{NEVER,ALWAYS,AUTO} ++ * ++ * Forces library to use stdout as terminal, non-terminal or use automatic ++ * detection (default). ++ * ++ * Returns: 0, a negative value in case of an error. ++ * ++ * Since: 2.29 ++ */ ++int scols_table_set_termforce(struct libscols_table *tb, int force) ++{ ++ if (!tb) ++ return -EINVAL; ++ tb->termforce = force; ++ return 0; ++} ++ ++/** ++ * scols_table_get_termforce: ++ * @tb: table ++ * ++ * Returns: SCOLS_TERMFORCE_{NEVER,ALWAYS,AUTO} or a negative value in case of an error. ++ * ++ * Since: 2.29 ++ */ ++int scols_table_get_termforce(const struct libscols_table *tb) ++{ ++ return tb->termforce; ++} ++ ++/** ++ * scols_table_set_termwidth ++ * @tb: table ++ * @width: terminal width ++ * ++ * The library automatically detects terminal width or defaults to 80 chars if ++ * detections is unsuccessful. This function override this behaviour. ++ * ++ * Returns: 0, a negative value in case of an error. ++ * ++ * Since: 2.29 ++ */ ++int scols_table_set_termwidth(struct libscols_table *tb, size_t width) ++{ ++ DBG(TAB, ul_debugobj(tb, "set terminatl width: %zu", width)); ++ tb->termwidth = width; + return 0; + } ++ ++/** ++ * scols_table_get_termwidth ++ * @tb: table ++ * ++ * Returns: terminal width. ++ */ ++size_t scols_table_get_termwidth(const struct libscols_table *tb) ++{ ++ return tb->termwidth; ++} ++ ++/** ++ * scols_table_set_termheight ++ * @tb: table ++ * @height: terminal height (number of lines) ++ * ++ * The library automatically detects terminal height or defaults to 24 lines if ++ * detections is unsuccessful. This function override this behaviour. ++ * ++ * Returns: 0, a negative value in case of an error. ++ * ++ * Since: 2.31 ++ */ ++int scols_table_set_termheight(struct libscols_table *tb, size_t height) ++{ ++ DBG(TAB, ul_debugobj(tb, "set terminatl height: %zu", height)); ++ tb->termheight = height; ++ return 0; ++} ++ ++/** ++ * scols_table_get_termheight ++ * @tb: table ++ * ++ * Returns: terminal height (number of lines). ++ * ++ * Since: 2.31 ++ */ ++size_t scols_table_get_termheight(const struct libscols_table *tb) ++{ ++ return tb->termheight; ++} +diff --git a/libsmartcols/src/table_print.c b/libsmartcols/src/table_print.c +index c9f3d8f4b..10126fd79 100644 +--- a/libsmartcols/src/table_print.c ++++ b/libsmartcols/src/table_print.c +@@ -2,6 +2,7 @@ + * table.c - functions handling the data at the table level + * + * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com> ++ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. +@@ -10,7 +11,7 @@ + /** + * SECTION: table_print + * @title: Table print +- * @short_description: table print API ++ * @short_description: output functions + * + * Table output API. + */ +@@ -21,13 +22,30 @@ + #include <termios.h> + #include <ctype.h> + +-#include "nls.h" + #include "mbsalign.h" +-#include "widechar.h" +-#include "ttyutils.h" + #include "carefulputc.h" + #include "smartcolsP.h" + ++#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ") ++#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n") ++ ++/* Fallback for symbols ++ * ++ * Note that by default library define all the symbols, but in case user does ++ * not define all symbols or if we extended the symbols struct then we need ++ * fallback to be more robust and backwardly compatible. ++ */ ++#define titlepadding_symbol(tb) ((tb)->symbols->title_padding ? (tb)->symbols->title_padding : " ") ++#define branch_symbol(tb) ((tb)->symbols->branch ? (tb)->symbols->branch : "|-") ++#define vertical_symbol(tb) ((tb)->symbols->vert ? (tb)->symbols->vert : "| ") ++#define right_symbol(tb) ((tb)->symbols->right ? (tb)->symbols->right : "`-") ++ ++#define cellpadding_symbol(tb) ((tb)->padding_debug ? "." : \ ++ ((tb)->symbols->cell_padding ? (tb)->symbols->cell_padding: " ")) ++ ++#define want_repeat_header(tb) (!(tb)->header_repeat || (tb)->header_next <= (tb)->termlines_used) ++ ++ + /* This is private struct to work with output data */ + struct libscols_buffer { + char *begin; /* begin of the buffer */ +@@ -88,7 +106,6 @@ static int buffer_append_data(struct libscols_buffer *buf, const char *str) + + if (maxsz <= sz) + return -EINVAL; +- + memcpy(buf->cur, str, sz + 1); + buf->cur += sz; + return 0; +@@ -100,7 +117,7 @@ static int buffer_set_data(struct libscols_buffer *buf, const char *str) + return rc ? rc : buffer_append_data(buf, str); + } + +-/* save the current buffer possition to art_idx */ ++/* save the current buffer position to art_idx */ + static void buffer_set_art_index(struct libscols_buffer *buf) + { + if (buf) { +@@ -115,7 +132,10 @@ static char *buffer_get_data(struct libscols_buffer *buf) + } + + /* 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) ++static char *buffer_get_safe_data(struct libscols_table *tb, ++ struct libscols_buffer *buf, ++ size_t *cells, ++ const char *safechars) + { + char *data = buffer_get_data(buf); + char *res = NULL; +@@ -129,7 +149,14 @@ static char *buffer_get_safe_data(struct libscols_buffer *buf, size_t *cells) + goto nothing; + } + +- res = mbs_safe_encode_to_buffer(data, cells, buf->encdata); ++ if (tb->no_encode) { ++ *cells = mbs_safe_width(data); ++ strcpy(buf->encdata, data); ++ res = buf->encdata; ++ } else { ++ res = mbs_safe_encode_to_buffer(data, cells, buf->encdata, safechars); ++ } ++ + if (!res || !*cells || *cells == (size_t) -1) + goto nothing; + return res; +@@ -151,11 +178,257 @@ static size_t buffer_get_safe_art_size(struct libscols_buffer *buf) + return bytes; + } + +-#define is_last_column(_tb, _cl) \ +- list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns) ++/* 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); + +-#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ") +-#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n") ++ 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 = vertical_symbol(tb); ++ ++ return buffer_append_data(buf, art); ++} ++ ++static int is_last_column(struct libscols_column *cl) ++{ ++ int rc = list_entry_is_last(&cl->cl_columns, &cl->table->tb_columns); ++ struct libscols_column *next; ++ ++ if (rc) ++ return 1; ++ ++ next = list_entry(cl->cl_columns.next, struct libscols_column, cl_columns); ++ if (next && scols_column_is_hidden(next) && is_last_column(next)) ++ return 1; ++ return 0; ++} ++ ++ ++static int has_pending_data(struct libscols_table *tb) ++{ ++ struct libscols_column *cl; ++ struct libscols_iter itr; ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ if (scols_column_is_hidden(cl)) ++ continue; ++ if (cl->pending_data) ++ return 1; ++ } ++ return 0; ++} ++ ++/* print padding or ASCII-art instead of data of @cl */ ++static void print_empty_cell(struct libscols_table *tb, ++ struct libscols_column *cl, ++ struct libscols_line *ln, /* optional */ ++ size_t bufsz) ++{ ++ size_t len_pad = 0; /* in screen cells as opposed to bytes */ ++ ++ /* generate tree ASCII-art rather than padding */ ++ if (ln && scols_column_is_tree(cl)) { ++ if (!ln->parent) { ++ /* only print symbols->vert if followed by child */ ++ if (!list_empty(&ln->ln_branch)) { ++ fputs(vertical_symbol(tb), tb->out); ++ len_pad = mbs_safe_width(vertical_symbol(tb)); ++ } ++ } else { ++ /* use the same draw function as though we were intending to draw an L-shape */ ++ struct libscols_buffer *art = new_buffer(bufsz); ++ char *data; ++ ++ if (art) { ++ /* whatever the rc, len_pad will be sensible */ ++ line_ascii_art_to_buffer(tb, ln, art); ++ if (!list_empty(&ln->ln_branch) && has_pending_data(tb)) ++ buffer_append_data(art, vertical_symbol(tb)); ++ data = buffer_get_safe_data(tb, art, &len_pad, NULL); ++ if (data && len_pad) ++ fputs(data, tb->out); ++ free_buffer(art); ++ } ++ } ++ } ++ ++ if (is_last_column(cl)) ++ return; ++ ++ /* fill rest of cell with space */ ++ for(; len_pad < cl->width; ++len_pad) ++ fputs(cellpadding_symbol(tb), tb->out); ++ ++ fputs(colsep(tb), tb->out); ++} ++ ++ ++static const char *get_cell_color(struct libscols_table *tb, ++ struct libscols_column *cl, ++ struct libscols_line *ln, /* optional */ ++ struct libscols_cell *ce) /* optional */ ++{ ++ const char *color = NULL; ++ ++ if (tb && tb->colors_wanted) { ++ if (ce) ++ color = ce->color; ++ if (ln && !color) ++ color = ln->color; ++ if (!color) ++ color = cl->color; ++ } ++ return color; ++} ++ ++/* Fill the start of a line with padding (or with tree ascii-art). ++ * ++ * This is necessary after a long non-truncated column, as this requires the ++ * next column to be printed on the next line. For example (see 'DDD'): ++ * ++ * aaa bbb ccc ddd eee ++ * AAA BBB CCCCCCC ++ * DDD EEE ++ * ^^^^^^^^^^^^ ++ * new line padding ++ */ ++static void print_newline_padding(struct libscols_table *tb, ++ struct libscols_column *cl, ++ struct libscols_line *ln, /* optional */ ++ size_t bufsz) ++{ ++ size_t i; ++ ++ assert(tb); ++ assert(cl); ++ ++ fputs(linesep(tb), tb->out); /* line break */ ++ tb->termlines_used++; ++ ++ /* fill cells after line break */ ++ for (i = 0; i <= (size_t) cl->seqnum; i++) ++ print_empty_cell(tb, scols_table_get_column(tb, i), ln, bufsz); ++} ++ ++/* ++ * Pending data ++ * ++ * The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is ++ * printed as usually and output is truncated to match column width. ++ * ++ * The rest of the long text is printed on next extra line(s). The extra lines ++ * don't exist in the table (not represented by libscols_line). The data for ++ * the extra lines are stored in libscols_column->pending_data_buf and the ++ * function print_line() adds extra lines until the buffer is not empty in all ++ * columns. ++ */ ++ ++/* set data that will be printed by extra lines */ ++static int set_pending_data(struct libscols_column *cl, const char *data, size_t sz) ++{ ++ char *p = NULL; ++ ++ if (data && *data) { ++ DBG(COL, ul_debugobj(cl, "setting pending data")); ++ assert(sz); ++ p = strdup(data); ++ if (!p) ++ return -ENOMEM; ++ } ++ ++ free(cl->pending_data_buf); ++ cl->pending_data_buf = p; ++ cl->pending_data_sz = sz; ++ cl->pending_data = cl->pending_data_buf; ++ return 0; ++} ++ ++/* the next extra line has been printed, move pending data cursor */ ++static int step_pending_data(struct libscols_column *cl, size_t bytes) ++{ ++ DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes)); ++ ++ if (bytes >= cl->pending_data_sz) ++ return set_pending_data(cl, NULL, 0); ++ ++ cl->pending_data += bytes; ++ cl->pending_data_sz -= bytes; ++ return 0; ++} ++ ++/* print next pending data for the column @cl */ ++static int print_pending_data( ++ struct libscols_table *tb, ++ struct libscols_column *cl, ++ struct libscols_line *ln, /* optional */ ++ struct libscols_cell *ce) ++{ ++ const char *color = get_cell_color(tb, cl, ln, ce); ++ size_t width = cl->width, bytes; ++ size_t len = width, i; ++ char *data; ++ char *nextchunk = NULL; ++ ++ if (!cl->pending_data) ++ return 0; ++ if (!width) ++ return -EINVAL; ++ ++ DBG(COL, ul_debugobj(cl, "printing pending data")); ++ ++ data = strdup(cl->pending_data); ++ if (!data) ++ goto err; ++ ++ if (scols_column_is_customwrap(cl) ++ && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) { ++ bytes = nextchunk - data; ++ ++ len = mbs_safe_nwidth(data, bytes, NULL); ++ } else ++ bytes = mbs_truncate(data, &len); ++ ++ if (bytes == (size_t) -1) ++ goto err; ++ ++ if (bytes) ++ step_pending_data(cl, bytes); ++ ++ if (color) ++ fputs(color, tb->out); ++ fputs(data, tb->out); ++ if (color) ++ fputs(UL_COLOR_RESET, tb->out); ++ free(data); ++ ++ if (is_last_column(cl)) ++ return 0; ++ ++ for (i = len; i < width; i++) ++ fputs(cellpadding_symbol(tb), tb->out); /* padding */ ++ ++ fputs(colsep(tb), tb->out); /* columns separator */ ++ return 0; ++err: ++ free(data); ++ return -errno; ++} + + static int print_data(struct libscols_table *tb, + struct libscols_column *cl, +@@ -165,76 +438,120 @@ static int print_data(struct libscols_table *tb, + { + size_t len = 0, i, width, bytes; + const char *color = NULL; +- char *data; ++ char *data, *nextchunk; ++ int is_last; + + 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)) { ++ is_last = is_last_column(cl); ++ ++ switch (tb->format) { ++ case SCOLS_FMT_RAW: + fputs_nonblank(data, tb->out); +- if (!is_last_column(tb, cl)) ++ if (!is_last) + fputs(colsep(tb), tb->out); + return 0; +- } + +- /* NAME=value mode */ +- if (scols_table_is_export(tb)) { ++ case SCOLS_FMT_EXPORT: + fprintf(tb->out, "%s=", scols_cell_get_data(&cl->header)); + fputs_quoted(data, tb->out); +- if (!is_last_column(tb, cl)) ++ if (!is_last) + 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; ++ case SCOLS_FMT_JSON: ++ fputs_quoted_json_lower(scols_cell_get_data(&cl->header), tb->out); ++ fputs(":", tb->out); ++ switch (cl->json_type) { ++ case SCOLS_JSON_STRING: ++ if (!*data) ++ fputs("null", tb->out); ++ else ++ fputs_quoted_json(data, tb->out); ++ break; ++ case SCOLS_JSON_NUMBER: ++ if (!*data) ++ fputs("null", tb->out); ++ else ++ fputs(data, tb->out); ++ break; ++ case SCOLS_JSON_BOOLEAN: ++ fputs(!*data ? "false" : ++ *data == '0' ? "false" : ++ *data == 'N' || *data == 'n' ? "false" : "true", ++ tb->out); ++ break; ++ } ++ if (!is_last) ++ fputs(", ", tb->out); ++ return 0; ++ ++ case SCOLS_FMT_HUMAN: ++ break; /* continue below */ + } + +- /* encode, note that 'len' and 'width' are number of cells, not bytes */ +- data = buffer_get_safe_data(buf, &len); ++ color = get_cell_color(tb, cl, ln, ce); ++ ++ /* Encode. Note that 'len' and 'width' are number of cells, not bytes. ++ */ ++ data = buffer_get_safe_data(tb, buf, &len, scols_column_get_safechars(cl)); + if (!data) + data = ""; +- width = cl->width; + bytes = strlen(data); ++ width = cl->width; ++ ++ /* custom multi-line cell based */ ++ if (*data && scols_column_is_customwrap(cl) ++ && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) { ++ set_pending_data(cl, nextchunk, bytes - (nextchunk - data)); ++ bytes = nextchunk - data; ++ len = mbs_safe_nwidth(data, bytes, NULL); ++ } + +- if (is_last_column(tb, cl) && len < width && !scols_table_is_maxout(tb)) ++ if (is_last ++ && len < width ++ && !scols_table_is_maxout(tb) ++ && !scols_column_is_right(cl)) + 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; +- } ++ /* standard multi-line cell */ ++ if (len > width && scols_column_is_wrap(cl) ++ && !scols_column_is_customwrap(cl)) { ++ set_pending_data(cl, data, bytes); ++ ++ len = width; ++ bytes = mbs_truncate(data, &len); ++ if (bytes != (size_t) -1 && bytes > 0) ++ step_pending_data(cl, bytes); ++ } ++ ++ if (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); ++ for (i = len; i < width; i++) ++ fputs(cellpadding_symbol(tb), tb->out); ++ fputs(data, tb->out); + if (color) + fputs(UL_COLOR_RESET, tb->out); +- if (len < xw) +- len = xw; ++ len = width; ++ + } else if (color) { + char *p = data; + size_t art = buffer_get_safe_art_size(buf); +@@ -252,46 +569,17 @@ static int print_data(struct libscols_table *tb, + 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; ++ fputs(cellpadding_symbol(tb), tb->out); /* padding */ + +- assert(ln); +- assert(buf); +- +- if (!ln->parent) ++ if (is_last) + 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 = " "; ++ if (len > width && !scols_column_is_trunc(cl)) ++ print_newline_padding(tb, cl, ln, buf->bufsz); /* next column starts on next line */ + else +- art = tb->symbols->vert; ++ fputs(colsep(tb), tb->out); /* columns separator */ + +- return buffer_append_data(buf, art); ++ return 0; + } + + static int cell_to_buffer(struct libscols_table *tb, +@@ -322,13 +610,13 @@ static int cell_to_buffer(struct libscols_table *tb, + /* + * Tree stuff + */ +- if (ln->parent) { ++ if (ln->parent && !scols_table_is_json(tb)) { + 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); ++ rc = buffer_append_data(buf, right_symbol(tb)); + else if (!rc) +- rc = buffer_append_data(buf, tb->symbols->branch); ++ rc = buffer_append_data(buf, branch_symbol(tb)); + if (!rc) + buffer_set_art_index(buf); + } +@@ -338,34 +626,252 @@ static int cell_to_buffer(struct libscols_table *tb, + return rc; + } + ++static void fput_indent(struct libscols_table *tb) ++{ ++ int i; ++ ++ for (i = 0; i <= tb->indent; i++) ++ fputs(" ", tb->out); ++} ++ ++static void fput_table_open(struct libscols_table *tb) ++{ ++ tb->indent = 0; ++ ++ if (scols_table_is_json(tb)) { ++ fputc('{', tb->out); ++ fputs(linesep(tb), tb->out); ++ ++ fput_indent(tb); ++ fputs_quoted(tb->name, tb->out); ++ fputs(": [", tb->out); ++ fputs(linesep(tb), tb->out); ++ ++ tb->indent++; ++ tb->indent_last_sep = 1; ++ } ++} ++ ++static void fput_table_close(struct libscols_table *tb) ++{ ++ tb->indent--; ++ ++ if (scols_table_is_json(tb)) { ++ fput_indent(tb); ++ fputc(']', tb->out); ++ tb->indent--; ++ fputs(linesep(tb), tb->out); ++ fputc('}', tb->out); ++ tb->indent_last_sep = 1; ++ } ++} ++ ++static void fput_children_open(struct libscols_table *tb) ++{ ++ if (scols_table_is_json(tb)) { ++ fputc(',', tb->out); ++ fputs(linesep(tb), tb->out); ++ fput_indent(tb); ++ fputs("\"children\": [", tb->out); ++ } ++ /* between parent and child is separator */ ++ fputs(linesep(tb), tb->out); ++ tb->indent_last_sep = 1; ++ tb->indent++; ++ tb->termlines_used++; ++} ++ ++static void fput_children_close(struct libscols_table *tb) ++{ ++ tb->indent--; ++ ++ if (scols_table_is_json(tb)) { ++ fput_indent(tb); ++ fputc(']', tb->out); ++ fputs(linesep(tb), tb->out); ++ tb->indent_last_sep = 1; ++ } ++} ++ ++static void fput_line_open(struct libscols_table *tb) ++{ ++ if (scols_table_is_json(tb)) { ++ fput_indent(tb); ++ fputc('{', tb->out); ++ tb->indent_last_sep = 0; ++ } ++ tb->indent++; ++} ++ ++static void fput_line_close(struct libscols_table *tb, int last, int last_in_table) ++{ ++ tb->indent--; ++ if (scols_table_is_json(tb)) { ++ if (tb->indent_last_sep) ++ fput_indent(tb); ++ fputs(last ? "}" : "},", tb->out); ++ if (!tb->no_linesep) ++ fputs(linesep(tb), tb->out); ++ ++ } else if (tb->no_linesep == 0 && last_in_table == 0) { ++ fputs(linesep(tb), tb->out); ++ tb->termlines_used++; ++ } ++ ++ tb->indent_last_sep = 1; ++} ++ + /* +- * 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. ++ * Prints data. Data can be printed in more formats (raw, NAME=xxx pairs), and ++ * control and non-printable characters can be encoded in the \x?? encoding. + */ + static int print_line(struct libscols_table *tb, + struct libscols_line *ln, + struct libscols_buffer *buf) + { +- int rc = 0; ++ int rc = 0, pending = 0; + struct libscols_column *cl; + struct libscols_iter itr; + + assert(ln); + +- DBG(TAB, ul_debugobj(tb, "printing line, line=%p, buff=%p", ln, buf)); ++ DBG(TAB, ul_debugobj(tb, "printing line")); + ++ /* regular line */ + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { ++ if (scols_column_is_hidden(cl)) ++ continue; + rc = cell_to_buffer(tb, ln, cl, buf); +- if (!rc) ++ if (rc == 0) + rc = print_data(tb, cl, ln, + scols_line_get_cell(ln, cl->seqnum), + buf); ++ if (rc == 0 && cl->pending_data) ++ pending = 1; ++ } ++ ++ /* extra lines of the multi-line cells */ ++ while (rc == 0 && pending) { ++ pending = 0; ++ fputs(linesep(tb), tb->out); ++ tb->termlines_used++; ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { ++ if (scols_column_is_hidden(cl)) ++ continue; ++ if (cl->pending_data) { ++ rc = print_pending_data(tb, cl, ln, scols_line_get_cell(ln, cl->seqnum)); ++ if (rc == 0 && cl->pending_data) ++ pending = 1; ++ } else ++ print_empty_cell(tb, cl, ln, buf->bufsz); ++ } + } + +- if (rc == 0) +- fputs(linesep(tb), tb->out); +- return 0; ++ return 0; ++} ++ ++static int print_title(struct libscols_table *tb) ++{ ++ int rc, color = 0; ++ mbs_align_t align; ++ size_t width, len = 0, bufsz, titlesz; ++ char *title = NULL, *buf = NULL; ++ ++ assert(tb); ++ ++ if (!tb->title.data) ++ return 0; ++ ++ DBG(TAB, ul_debugobj(tb, "printing title")); ++ ++ /* encode data */ ++ if (tb->no_encode) { ++ len = bufsz = strlen(tb->title.data) + 1; ++ buf = strdup(tb->title.data); ++ if (!buf) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ } else { ++ bufsz = mbs_safe_encode_size(strlen(tb->title.data)) + 1; ++ if (bufsz == 1) { ++ DBG(TAB, ul_debugobj(tb, "title is empty string -- ignore")); ++ return 0; ++ } ++ buf = malloc(bufsz); ++ if (!buf) { ++ rc = -ENOMEM; ++ goto done; ++ } ++ ++ if (!mbs_safe_encode_to_buffer(tb->title.data, &len, buf, NULL) || ++ !len || len == (size_t) -1) { ++ rc = -EINVAL; ++ goto done; ++ } ++ } ++ ++ /* truncate and align */ ++ width = tb->is_term ? tb->termwidth : 80; ++ titlesz = width + bufsz; ++ ++ title = malloc(titlesz); ++ if (!title) { ++ rc = -EINVAL; ++ goto done; ++ } ++ ++ switch (scols_cell_get_alignment(&tb->title)) { ++ case SCOLS_CELL_FL_RIGHT: ++ align = MBS_ALIGN_RIGHT; ++ break; ++ case SCOLS_CELL_FL_CENTER: ++ align = MBS_ALIGN_CENTER; ++ break; ++ case SCOLS_CELL_FL_LEFT: ++ default: ++ align = MBS_ALIGN_LEFT; ++ /* ++ * Don't print extra blank chars after the title if on left ++ * (that's same as we use for the last column in the table). ++ */ ++ if (len < width ++ && !scols_table_is_maxout(tb) ++ && isblank(*titlepadding_symbol(tb))) ++ width = len; ++ break; ++ ++ } ++ ++ /* copy from buf to title and align to width with title_padding */ ++ rc = mbsalign_with_padding(buf, title, titlesz, ++ &width, align, ++ 0, (int) *titlepadding_symbol(tb)); ++ ++ if (rc == -1) { ++ rc = -EINVAL; ++ goto done; ++ } ++ ++ if (tb->colors_wanted && tb->title.color) ++ color = 1; ++ if (color) ++ fputs(tb->title.color, tb->out); ++ ++ fputs(title, tb->out); ++ ++ if (color) ++ fputs(UL_COLOR_RESET, tb->out); ++ ++ fputc('\n', tb->out); ++ rc = 0; ++done: ++ free(buf); ++ free(title); ++ DBG(TAB, ul_debugobj(tb, "printing title done [rc=%d]", rc)); ++ return rc; + } + + static int print_header(struct libscols_table *tb, struct libscols_buffer *buf) +@@ -376,86 +882,139 @@ static int print_header(struct libscols_table *tb, struct libscols_buffer *buf) + + assert(tb); + +- if (scols_table_is_noheadings(tb) || ++ if ((tb->header_printed == 1 && tb->header_repeat == 0) || ++ scols_table_is_noheadings(tb) || + scols_table_is_export(tb) || ++ scols_table_is_json(tb) || + list_empty(&tb->tb_lines)) + return 0; + + DBG(TAB, ul_debugobj(tb, "printing header")); + +- /* set width according to the size of data +- */ ++ /* set the width according to the size of the data */ + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { ++ if (scols_column_is_hidden(cl)) ++ continue; + 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) ++ if (rc == 0) { + fputs(linesep(tb), tb->out); ++ tb->termlines_used++; ++ } ++ ++ tb->header_printed = 1; ++ tb->header_next = tb->termlines_used + tb->termheight; ++ if (tb->header_repeat) ++ DBG(TAB, ul_debugobj(tb, "\tnext header: %zu [current=%zu]", ++ tb->header_next, tb->termlines_used)); + return rc; + } + +-static int print_table(struct libscols_table *tb, struct libscols_buffer *buf) ++ ++static int print_range( struct libscols_table *tb, ++ struct libscols_buffer *buf, ++ struct libscols_iter *itr, ++ struct libscols_line *end) + { +- int rc; ++ int rc = 0; + struct libscols_line *ln; +- struct libscols_iter itr; + + assert(tb); ++ DBG(TAB, ul_debugobj(tb, "printing range")); + +- rc = print_header(tb, buf); ++ while (rc == 0 && scols_table_next_line(tb, itr, &ln) == 0) { + +- scols_reset_iter(&itr, SCOLS_ITER_FORWARD); +- while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) ++ int last = scols_iter_is_last(itr); ++ ++ fput_line_open(tb); + rc = print_line(tb, ln, buf); ++ fput_line_close(tb, last, last); ++ ++ if (end && ln == end) ++ break; ++ ++ if (!last && want_repeat_header(tb)) ++ print_header(tb, buf); ++ } + + return rc; ++ ++} ++ ++static int print_table(struct libscols_table *tb, struct libscols_buffer *buf) ++{ ++ struct libscols_iter itr; ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ return print_range(tb, buf, &itr, NULL); + } + ++ + static int print_tree_line(struct libscols_table *tb, + struct libscols_line *ln, +- struct libscols_buffer *buf) ++ struct libscols_buffer *buf, ++ int last, ++ int last_in_table) + { + int rc; +- struct list_head *p; + ++ /* print the line */ ++ fput_line_open(tb); + rc = print_line(tb, ln, buf); + if (rc) +- return rc; +- if (list_empty(&ln->ln_branch)) +- return 0; ++ goto done; + +- /* 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; ++ /* print children */ ++ if (!list_empty(&ln->ln_branch)) { ++ struct list_head *p; ++ ++ fput_children_open(tb); ++ ++ /* print all children */ ++ list_for_each(p, &ln->ln_branch) { ++ struct libscols_line *chld = ++ list_entry(p, struct libscols_line, ln_children); ++ int last_child = p->next == &ln->ln_branch; ++ ++ rc = print_tree_line(tb, chld, buf, last_child, last_in_table && last_child); ++ if (rc) ++ goto done; ++ } ++ ++ fput_children_close(tb); + } + ++ if (list_empty(&ln->ln_branch) || scols_table_is_json(tb)) ++ fput_line_close(tb, last, last_in_table); ++done: + return rc; + } + + static int print_tree(struct libscols_table *tb, struct libscols_buffer *buf) + { +- int rc; +- struct libscols_line *ln; ++ int rc = 0; ++ struct libscols_line *ln, *last = NULL; + 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 (scols_table_next_line(tb, &itr, &ln) == 0) ++ if (!last || !ln->parent) ++ last = ln; + + 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); ++ rc = print_tree_line(tb, ln, buf, ln == last, ln == last); + } + + return rc; +@@ -463,9 +1022,14 @@ static int print_tree(struct libscols_table *tb, struct libscols_buffer *buf) + + static void dbg_column(struct libscols_table *tb, struct libscols_column *cl) + { ++ if (scols_column_is_hidden(cl)) { ++ DBG(COL, ul_debugobj(cl, "%s (hidden) ignored", cl->header.data)); ++ return; ++ } ++ + DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, " + "hint=%d, avg=%zu, max=%zu, min=%zu, " +- "extreme=%s", ++ "extreme=%s %s", + + cl->header.data, cl->seqnum, cl->width, + cl->width_hint > 1 ? (int) cl->width_hint : +@@ -473,7 +1037,8 @@ static void dbg_column(struct libscols_table *tb, struct libscols_column *cl) + cl->width_avg, + cl->width_max, + cl->width_min, +- cl->is_extreme ? "yes" : "not")); ++ cl->is_extreme ? "yes" : "not", ++ cl->flags & SCOLS_FL_TRUNC ? "trunc" : "")); + } + + static void dbg_columns(struct libscols_table *tb) +@@ -486,14 +1051,15 @@ static void dbg_columns(struct libscols_table *tb) + 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. ++ * For the SCOLS_FL_NOEXTREMES columns it is possible to call this function ++ * two times. The first pass counts the width and average width. If the column ++ * contains fields that are too large (a width greater than 2 * average) then ++ * the column is marked as "extreme". In the second pass all extreme fields ++ * are ignored and the column width is counted from non-extreme fields only. + */ + static int count_column_width(struct libscols_table *tb, + struct libscols_column *cl, +@@ -508,6 +1074,19 @@ static int count_column_width(struct libscols_table *tb, + assert(cl); + + cl->width = 0; ++ if (!cl->width_min) { ++ if (cl->width_hint < 1 && scols_table_is_maxout(tb) && tb->is_term) { ++ cl->width_min = (size_t) (cl->width_hint * tb->termwidth); ++ if (cl->width_min && !is_last_column(cl)) ++ cl->width_min--; ++ } ++ if (scols_cell_get_data(&cl->header)) { ++ size_t len = mbs_safe_width(scols_cell_get_data(&cl->header)); ++ cl->width_min = max(cl->width_min, len); ++ } ++ if (!cl->width_min) ++ cl->width_min = 1; ++ } + + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_table_next_line(tb, &itr, &ln) == 0) { +@@ -516,15 +1095,20 @@ static int count_column_width(struct libscols_table *tb, + + rc = cell_to_buffer(tb, ln, cl, buf); + if (rc) +- return rc; ++ goto done; + + data = buffer_get_data(buf); +- len = data ? mbs_safe_width(data) : 0; ++ ++ if (!data) ++ len = 0; ++ else if (scols_column_is_customwrap(cl)) ++ len = cl->wrap_chunksize(cl, data, cl->wrapfunc_data); ++ else ++ len = mbs_safe_width(data); + + if (len == (size_t) -1) /* ignore broken multibyte strings */ + len = 0; +- if (len > cl->width_max) +- cl->width_max = len; ++ cl->width_max = max(len, cl->width_max); + + if (cl->is_extreme && len > cl->width_avg * 2) + continue; +@@ -532,81 +1116,105 @@ static int count_column_width(struct libscols_table *tb, + sum += len; + count++; + } +- if (len > cl->width) +- cl->width = len; ++ cl->width = max(len, cl->width); ++ if (scols_column_is_tree(cl)) { ++ size_t treewidth = buffer_get_safe_art_size(buf); ++ cl->width_treeart = max(cl->width_treeart, treewidth); ++ } + } + + 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 */ ++ /* use absolute 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; + ++done: + ON_DBG(COL, dbg_column(tb, cl)); + return rc; + } + +- + /* +- * This is core of the scols_* voodo... ++ * This is core of the scols_* voodoo... + */ + 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; ++ size_t width = 0, width_min = 0; /* output width */ ++ int stage, rc = 0; + int extremes = 0; ++ size_t colsepsz; + + + DBG(TAB, ul_debugobj(tb, "recounting widths (termwidth=%zu)", tb->termwidth)); + ++ colsepsz = mbs_safe_width(colsep(tb)); ++ + /* set basic columns width + */ + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ int is_last; ++ ++ if (scols_column_is_hidden(cl)) ++ continue; + rc = count_column_width(tb, cl, buf); + if (rc) +- return rc; ++ goto done; ++ ++ is_last = is_last_column(cl); + +- width += cl->width + (is_last_column(tb, cl) ? 0 : 1); ++ width += cl->width + (is_last ? 0 : colsepsz); /* separator for non-last column */ ++ width_min += cl->width_min + (is_last ? 0 : colsepsz); + extremes += cl->is_extreme; + } + +- if (!tb->is_term) +- return 0; ++ if (!tb->is_term) { ++ DBG(TAB, ul_debugobj(tb, " non-terminal output")); ++ goto done; ++ } + +- /* reduce columns with extreme fields +- */ ++ /* be paranoid */ ++ if (width_min > tb->termwidth && scols_table_is_maxout(tb)) { ++ DBG(TAB, ul_debugobj(tb, " min width larger than terminal! [width=%zu, term=%zu]", width_min, tb->termwidth)); ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (width_min > tb->termwidth ++ && scols_table_next_column(tb, &itr, &cl) == 0) { ++ if (scols_column_is_hidden(cl)) ++ continue; ++ width_min--; ++ cl->width_min--; ++ } ++ DBG(TAB, ul_debugobj(tb, " min width reduced to %zu", width_min)); ++ } ++ ++ /* reduce columns with extreme fields */ + if (width > tb->termwidth && extremes) { +- DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)")); ++ 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) ++ if (!cl->is_extreme || scols_column_is_hidden(cl)) + continue; + + org_width = cl->width; + rc = count_column_width(tb, cl, buf); + if (rc) +- return rc; ++ goto done; + + if (org_width > cl->width) + width -= org_width - cl->width; +@@ -617,17 +1225,17 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf + + if (width < tb->termwidth) { + if (extremes) { +- DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)")); ++ 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) ++ if (!cl->is_extreme || scols_column_is_hidden(cl)) + continue; + +- /* this column is tooo large, ignore? ++ /* this column is too large, ignore? + if (cl->width_max - cl->width > + (tb->termwidth - width)) + continue; +@@ -646,12 +1254,14 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf + } + + if (width < tb->termwidth && scols_table_is_maxout(tb)) { +- DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)")); ++ DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)")); + +- /* try enlarge all columns */ ++ /* try enlarging all columns */ + while (width < tb->termwidth) { + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ if (scols_column_is_hidden(cl)) ++ continue; + cl->width++; + width++; + if (width == tb->termwidth) +@@ -660,67 +1270,133 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf + } + } else if (width < tb->termwidth) { + /* enlarge the last column */ +- struct libscols_column *cl = list_entry( ++ struct libscols_column *col = list_entry( + tb->tb_columns.prev, struct libscols_column, cl_columns); + +- DBG(TAB, ul_debugobj(tb, " enlarge width (last column)")); ++ DBG(TAB, ul_debugobj(tb, " enlarge width (last column)")); + +- if (!scols_column_is_right(cl) && tb->termwidth - width > 0) { +- cl->width += tb->termwidth - width; ++ if (!scols_column_is_right(col) && tb->termwidth - width > 0) { ++ col->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 ++ /* bad, we have to reduce output width, this is done in three stages: ++ * ++ * 1) trunc relative with trunc flag if the column width is greater than ++ * expected column width (it means "width_hint * terminal_width"). ++ * ++ * 2) trunc all with trunc flag ++ * ++ * 3) trunc relative without trunc flag ++ * ++ * Note that SCOLS_FL_WRAP (if no custom wrap function is specified) is ++ * interpreted as SCOLS_FL_TRUNC. + */ +- trunc_only = 1; +- while (width > tb->termwidth) { +- size_t org = width; ++ for (stage = 1; width > tb->termwidth && stage <= 3; ) { ++ size_t org_width = width; + +- DBG(TAB, ul_debugobj(tb, " reduce width (current=%zu, " +- "wanted=%zu, mode=%s)", +- width, tb->termwidth, +- trunc_only ? "trunc-only" : "all-relative")); ++ DBG(TAB, ul_debugobj(tb, " reduce width - #%d stage (current=%zu, wanted=%zu)", ++ stage, width, tb->termwidth)); + + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ ++ int trunc_flag = 0; ++ ++ DBG(TAB, ul_debugobj(cl, " checking %s (width=%zu, treeart=%zu)", ++ cl->header.data, cl->width, cl->width_treeart)); ++ if (scols_column_is_hidden(cl)) ++ continue; + 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; ++ ++ /* never truncate if already minimal width */ + 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) { ++ /* never truncate the tree */ ++ if (scols_column_is_tree(cl) && width <= cl->width_treeart) ++ continue; ++ ++ /* nothing to truncate */ ++ if (cl->width == 0 || width == 0) ++ continue; ++ ++ trunc_flag = scols_column_is_trunc(cl) ++ || (scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl)); ++ ++ switch (stage) { ++ /* #1 stage - trunc relative with TRUNC flag */ ++ case 1: ++ if (!trunc_flag) /* ignore: missing flag */ ++ break; ++ if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */ ++ break; ++ if (cl->width < (size_t) (cl->width_hint * tb->termwidth)) /* ignore: smaller than expected width */ ++ break; ++ ++ DBG(TAB, ul_debugobj(tb, " reducing (relative with flag)")); + cl->width--; + width--; +- } +- /* truncate column with absolute size */ +- if (cl->width_hint > 1 && cl->width > 0 && width > 0 && +- !trunc_only) { ++ break; ++ ++ /* #2 stage - trunc all with TRUNC flag */ ++ case 2: ++ if (!trunc_flag) /* ignore: missing flag */ ++ break; ++ ++ DBG(TAB, ul_debugobj(tb, " reducing (all with flag)")); + cl->width--; + width--; ++ break; ++ ++ /* #3 stage - trunc relative without flag */ ++ case 3: ++ if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */ ++ break; ++ ++ DBG(TAB, ul_debugobj(tb, " reducing (relative without flag)")); ++ cl->width--; ++ width--; ++ break; + } + ++ /* hide zero width columns */ ++ if (cl->width == 0) ++ cl->flags |= SCOLS_FL_HIDDEN; + } +- if (org == width) { +- if (trunc_only) +- trunc_only = 0; +- else ++ ++ /* the current stage is without effect, go to the next */ ++ if (org_width == width) ++ stage++; ++ } ++ ++ /* ignore last column(s) or force last column to be truncated if ++ * nowrap mode enabled */ ++ if (tb->no_wrap && width > tb->termwidth) { ++ scols_reset_iter(&itr, SCOLS_ITER_BACKWARD); ++ while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ ++ if (scols_column_is_hidden(cl)) ++ continue; ++ if (width <= tb->termwidth) + break; ++ if (width - cl->width < tb->termwidth) { ++ size_t r = width - tb->termwidth; ++ ++ cl->flags |= SCOLS_FL_TRUNC; ++ cl->width -= r; ++ width -= r; ++ } else { ++ cl->flags |= SCOLS_FL_HIDDEN; ++ width -= cl->width + colsepsz; ++ } + } + } +- +- DBG(TAB, ul_debugobj(tb, " result: %zu", width)); ++done: ++ DBG(TAB, ul_debugobj(tb, " final width: %zu (rc=%d)", width, rc)); + ON_DBG(TAB, dbg_columns(tb)); + + return rc; +@@ -742,64 +1418,281 @@ static size_t strlen_line(struct libscols_line *ln) + return sz; + } + ++static void cleanup_printing(struct libscols_table *tb, struct libscols_buffer *buf) ++{ ++ if (!tb) ++ return; + ++ free_buffer(buf); + +-/** +- * 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) ++ if (tb->priv_symbols) { ++ scols_table_set_symbols(tb, NULL); ++ tb->priv_symbols = 0; ++ } ++} ++ ++static int initialize_printing(struct libscols_table *tb, struct libscols_buffer **buf) + { +- int rc = 0; +- size_t bufsz; ++ size_t bufsz, extra_bufsz = 0; + struct libscols_line *ln; + struct libscols_iter itr; +- struct libscols_buffer *buf; ++ int rc; + +- assert(tb); +- if (!tb) +- return -1; ++ DBG(TAB, ul_debugobj(tb, "initialize printing")); ++ *buf = NULL; + +- DBG(TAB, ul_debugobj(tb, "printing")); +- if (!tb->symbols) +- scols_table_set_symbols(tb, NULL); /* use default */ ++ if (!tb->symbols) { ++ rc = scols_table_set_default_symbols(tb); ++ if (rc) ++ goto err; ++ tb->priv_symbols = 1; ++ } else ++ tb->priv_symbols = 0; ++ ++ if (tb->format == SCOLS_FMT_HUMAN) ++ tb->is_term = tb->termforce == SCOLS_TERMFORCE_NEVER ? 0 : ++ tb->termforce == SCOLS_TERMFORCE_ALWAYS ? 1 : ++ isatty(STDOUT_FILENO); ++ ++ if (tb->is_term) { ++ size_t width = (size_t) scols_table_get_termwidth(tb); ++ ++ if (tb->termreduce > 0 && tb->termreduce < width) { ++ width -= tb->termreduce; ++ scols_table_set_termwidth(tb, width); ++ } ++ bufsz = width; ++ } else ++ bufsz = BUFSIZ; + +- 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; ++ if (!tb->is_term || tb->format != SCOLS_FMT_HUMAN || scols_table_is_tree(tb)) ++ tb->header_repeat = 0; + +- bufsz = tb->termwidth; ++ /* ++ * Estimate extra space necessary for tree, JSON or another output ++ * decoration. ++ */ ++ if (scols_table_is_tree(tb)) ++ extra_bufsz += tb->nlines * strlen(vertical_symbol(tb)); ++ ++ switch (tb->format) { ++ case SCOLS_FMT_RAW: ++ extra_bufsz += tb->ncols; /* separator between columns */ ++ break; ++ case SCOLS_FMT_JSON: ++ if (tb->format == SCOLS_FMT_JSON) ++ extra_bufsz += tb->nlines * 3; /* indention */ ++ /* fallthrough */ ++ case SCOLS_FMT_EXPORT: ++ { ++ struct libscols_column *cl; ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ ++ while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ if (scols_column_is_hidden(cl)) ++ continue; ++ extra_bufsz += strlen(scols_cell_get_data(&cl->header)); /* data */ ++ extra_bufsz += 2; /* separators */ ++ } ++ break; ++ } ++ case SCOLS_FMT_HUMAN: ++ break; ++ } + ++ /* ++ * Enlarge buffer if necessary, the buffer should be large enough to ++ * store line data and tree ascii art (or another decoration). ++ */ + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_table_next_line(tb, &itr, &ln) == 0) { +- size_t sz = strlen_line(ln); ++ size_t sz; ++ ++ sz = strlen_line(ln) + extra_bufsz; + if (sz > bufsz) + bufsz = sz; + } + +- buf = new_buffer(bufsz + 1); /* data + space for \0 */ +- if (!buf) +- return -ENOMEM; ++ *buf = new_buffer(bufsz + 1); /* data + space for \0 */ ++ if (!*buf) { ++ rc = -ENOMEM; ++ goto err; ++ } + +- if (!(scols_table_is_raw(tb) || scols_table_is_export(tb))) { +- rc = recount_widths(tb, buf); ++ if (tb->format == SCOLS_FMT_HUMAN) { ++ rc = recount_widths(tb, *buf); + if (rc != 0) ++ goto err; ++ } ++ ++ return 0; ++err: ++ cleanup_printing(tb, *buf); ++ return rc; ++} ++ ++/** ++ * scola_table_print_range: ++ * @tb: table ++ * @start: first printed line or NULL to print from the begin of the table ++ * @end: last printed line or NULL to print all from start. ++ * ++ * If the start is the first line in the table than prints table header too. ++ * The header is printed only once. This does not work for trees. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_table_print_range( struct libscols_table *tb, ++ struct libscols_line *start, ++ struct libscols_line *end) ++{ ++ struct libscols_buffer *buf = NULL; ++ struct libscols_iter itr; ++ int rc; ++ ++ if (scols_table_is_tree(tb)) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "printing range from API")); ++ ++ rc = initialize_printing(tb, &buf); ++ if (rc) ++ return rc; ++ ++ if (start) { ++ itr.direction = SCOLS_ITER_FORWARD; ++ itr.head = &tb->tb_lines; ++ itr.p = &start->ln_lines; ++ } else ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ ++ if (!start || itr.p == tb->tb_lines.next) { ++ rc = print_header(tb, buf); ++ if (rc) + goto done; + } + ++ rc = print_range(tb, buf, &itr, end); ++done: ++ cleanup_printing(tb, buf); ++ return rc; ++} ++ ++/** ++ * scols_table_print_range_to_string: ++ * @tb: table ++ * @start: first printed line or NULL to print from the beginning of the table ++ * @end: last printed line or NULL to print all from start. ++ * @data: pointer to the beginning of a memory area to print to ++ * ++ * The same as scols_table_print_range(), but prints to @data instead of ++ * stream. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++#ifdef HAVE_OPEN_MEMSTREAM ++int scols_table_print_range_to_string( struct libscols_table *tb, ++ struct libscols_line *start, ++ struct libscols_line *end, ++ char **data) ++{ ++ FILE *stream, *old_stream; ++ size_t sz; ++ int rc; ++ ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "printing range to string")); ++ ++ /* create a stream for output */ ++ stream = open_memstream(data, &sz); ++ if (!stream) ++ return -ENOMEM; ++ ++ old_stream = scols_table_get_stream(tb); ++ scols_table_set_stream(tb, stream); ++ rc = scols_table_print_range(tb, start, end); ++ fclose(stream); ++ scols_table_set_stream(tb, old_stream); ++ ++ return rc; ++} ++#else ++int scols_table_print_range_to_string( ++ struct libscols_table *tb __attribute__((__unused__)), ++ struct libscols_line *start __attribute__((__unused__)), ++ struct libscols_line *end __attribute__((__unused__)), ++ char **data __attribute__((__unused__))) ++{ ++ return -ENOSYS; ++} ++#endif ++ ++static int __scols_print_table(struct libscols_table *tb, int *is_empty) ++{ ++ int rc = 0; ++ struct libscols_buffer *buf = NULL; ++ ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "printing")); ++ if (is_empty) ++ *is_empty = 0; ++ ++ if (list_empty(&tb->tb_columns)) { ++ DBG(TAB, ul_debugobj(tb, "error -- no columns")); ++ return -EINVAL; ++ } ++ if (list_empty(&tb->tb_lines)) { ++ DBG(TAB, ul_debugobj(tb, "ignore -- no lines")); ++ if (is_empty) ++ *is_empty = 1; ++ return 0; ++ } ++ ++ tb->header_printed = 0; ++ rc = initialize_printing(tb, &buf); ++ if (rc) ++ return rc; ++ ++ fput_table_open(tb); ++ ++ if (tb->format == SCOLS_FMT_HUMAN) ++ print_title(tb); ++ ++ rc = print_header(tb, buf); ++ if (rc) ++ goto done; ++ + if (scols_table_is_tree(tb)) + rc = print_tree(tb, buf); + else + rc = print_table(tb, buf); + ++ fput_table_close(tb); + done: +- free_buffer(buf); ++ cleanup_printing(tb, buf); ++ return rc; ++} ++ ++/** ++ * scols_print_table: ++ * @tb: table ++ * ++ * Prints the table to the output stream and terminate by \n. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_print_table(struct libscols_table *tb) ++{ ++ int empty = 0; ++ int rc = __scols_print_table(tb, &empty); ++ ++ if (rc == 0 && !empty) ++ fputc('\n', tb->out); + return rc; + } + +@@ -812,10 +1705,10 @@ done: + * + * Returns: 0, a negative value in case of an error. + */ ++#ifdef HAVE_OPEN_MEMSTREAM + int scols_print_table_to_string(struct libscols_table *tb, char **data) + { +-#ifdef HAVE_OPEN_MEMSTREAM +- FILE *stream; ++ FILE *stream, *old_stream; + size_t sz; + int rc; + +@@ -829,13 +1722,20 @@ int scols_print_table_to_string(struct libscols_table *tb, char **data) + if (!stream) + return -ENOMEM; + ++ old_stream = scols_table_get_stream(tb); + scols_table_set_stream(tb, stream); +- rc = scols_print_table(tb); ++ rc = __scols_print_table(tb, NULL); + fclose(stream); ++ scols_table_set_stream(tb, old_stream); + + return rc; ++} + #else ++int scols_print_table_to_string( ++ struct libscols_table *tb __attribute__((__unused__)), ++ char **data __attribute__((__unused__))) ++{ + return -ENOSYS; +-#endif + } ++#endif + +-- +2.14.4 + diff --git a/SOURCES/0154-tests-backport-libsmartcols-tests.patch b/SOURCES/0154-tests-backport-libsmartcols-tests.patch new file mode 100644 index 0000000..e59618d --- /dev/null +++ b/SOURCES/0154-tests-backport-libsmartcols-tests.patch @@ -0,0 +1,990 @@ +From 83f79678dbb9cb48969968fa4df57d98c67a321d Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 31 May 2018 11:44:35 +0200 +Subject: [PATCH 154/173] tests: backport libsmartcols tests + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1561350 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + tests/commands.sh | 2 + + tests/expected/libsmartcols/fromfile | 1 + + .../libsmartcols/fromfile-column-separator | 11 + + tests/expected/libsmartcols/fromfile-export | 10 + + tests/expected/libsmartcols/fromfile-hidden | 11 + + tests/expected/libsmartcols/fromfile-noextremes | 12 ++ + tests/expected/libsmartcols/fromfile-raw | 11 + + tests/expected/libsmartcols/fromfile-right | 11 + + tests/expected/libsmartcols/fromfile-right-maxout | 11 + + tests/expected/libsmartcols/fromfile-strictwidth | 11 + + tests/expected/libsmartcols/fromfile-tree | 11 + + tests/expected/libsmartcols/fromfile-tree-end | 11 + + tests/expected/libsmartcols/fromfile-tree-json | 29 +++ + tests/expected/libsmartcols/fromfile-tree-middle | 11 + + tests/expected/libsmartcols/fromfile-trunc | 11 + + tests/expected/libsmartcols/fromfile-wrap | 17 ++ + tests/expected/libsmartcols/fromfile-wrap-tree | 18 ++ + tests/expected/libsmartcols/fromfile-wrapnl | 19 ++ + tests/expected/libsmartcols/fromfile-wrapnl-tree | 19 ++ + tests/expected/libsmartcols/title | 16 ++ + tests/ts/libsmartcols/files/col-hidden | 3 + + tests/ts/libsmartcols/files/col-id | 3 + + tests/ts/libsmartcols/files/col-name | 3 + + tests/ts/libsmartcols/files/col-noextremes | 3 + + tests/ts/libsmartcols/files/col-number | 3 + + tests/ts/libsmartcols/files/col-parent | 3 + + tests/ts/libsmartcols/files/col-strict | 3 + + tests/ts/libsmartcols/files/col-string | 3 + + tests/ts/libsmartcols/files/col-tree | 3 + + tests/ts/libsmartcols/files/col-trunc | 3 + + tests/ts/libsmartcols/files/col-wrap | 3 + + tests/ts/libsmartcols/files/col-wrapnl | 3 + + tests/ts/libsmartcols/files/data-id | 10 + + tests/ts/libsmartcols/files/data-number | 10 + + tests/ts/libsmartcols/files/data-number-tiny | 10 + + tests/ts/libsmartcols/files/data-parent | 10 + + tests/ts/libsmartcols/files/data-string | 10 + + tests/ts/libsmartcols/files/data-string-extreme | 10 + + tests/ts/libsmartcols/files/data-string-long | 10 + + tests/ts/libsmartcols/files/data-string-nl | 10 + + tests/ts/libsmartcols/fromfile | 240 +++++++++++++++++++++ + tests/ts/libsmartcols/title | 28 +++ + 42 files changed, 637 insertions(+) + create mode 100644 tests/expected/libsmartcols/fromfile + create mode 100644 tests/expected/libsmartcols/fromfile-column-separator + create mode 100644 tests/expected/libsmartcols/fromfile-export + create mode 100644 tests/expected/libsmartcols/fromfile-hidden + create mode 100644 tests/expected/libsmartcols/fromfile-noextremes + create mode 100644 tests/expected/libsmartcols/fromfile-raw + create mode 100644 tests/expected/libsmartcols/fromfile-right + create mode 100644 tests/expected/libsmartcols/fromfile-right-maxout + create mode 100644 tests/expected/libsmartcols/fromfile-strictwidth + create mode 100644 tests/expected/libsmartcols/fromfile-tree + create mode 100644 tests/expected/libsmartcols/fromfile-tree-end + create mode 100644 tests/expected/libsmartcols/fromfile-tree-json + create mode 100644 tests/expected/libsmartcols/fromfile-tree-middle + create mode 100644 tests/expected/libsmartcols/fromfile-trunc + create mode 100644 tests/expected/libsmartcols/fromfile-wrap + create mode 100644 tests/expected/libsmartcols/fromfile-wrap-tree + create mode 100644 tests/expected/libsmartcols/fromfile-wrapnl + create mode 100644 tests/expected/libsmartcols/fromfile-wrapnl-tree + create mode 100644 tests/expected/libsmartcols/title + create mode 100644 tests/ts/libsmartcols/files/col-hidden + create mode 100644 tests/ts/libsmartcols/files/col-id + create mode 100644 tests/ts/libsmartcols/files/col-name + create mode 100644 tests/ts/libsmartcols/files/col-noextremes + create mode 100644 tests/ts/libsmartcols/files/col-number + create mode 100644 tests/ts/libsmartcols/files/col-parent + create mode 100644 tests/ts/libsmartcols/files/col-strict + create mode 100644 tests/ts/libsmartcols/files/col-string + create mode 100644 tests/ts/libsmartcols/files/col-tree + create mode 100644 tests/ts/libsmartcols/files/col-trunc + create mode 100644 tests/ts/libsmartcols/files/col-wrap + create mode 100644 tests/ts/libsmartcols/files/col-wrapnl + create mode 100644 tests/ts/libsmartcols/files/data-id + create mode 100644 tests/ts/libsmartcols/files/data-number + create mode 100644 tests/ts/libsmartcols/files/data-number-tiny + create mode 100644 tests/ts/libsmartcols/files/data-parent + create mode 100644 tests/ts/libsmartcols/files/data-string + create mode 100644 tests/ts/libsmartcols/files/data-string-extreme + create mode 100644 tests/ts/libsmartcols/files/data-string-long + create mode 100644 tests/ts/libsmartcols/files/data-string-nl + create mode 100755 tests/ts/libsmartcols/fromfile + create mode 100755 tests/ts/libsmartcols/title + +diff --git a/tests/commands.sh b/tests/commands.sh +index e769b5dac..96b8dc97c 100644 +--- a/tests/commands.sh ++++ b/tests/commands.sh +@@ -20,6 +20,8 @@ TS_HELPER_PARTITIONS="$top_builddir/sample-partitions" + TS_HELPER_PATHS="$top_builddir/test_pathnames" + TS_HELPER_STRUTILS="$top_builddir/test_strutils" + TS_HELPER_SYSINFO="$top_builddir/test_sysinfo" ++TS_HELPER_LIBSMARTCOLS_FROMFILE="$top_builddir/sample-scols-fromfile" ++TS_HELPER_LIBSMARTCOLS_TITLE="$top_builddir/sample-scols-title" + + # paths to commands + TS_CMD_BLKID=${TS_CMD_BLKID-"$top_builddir/blkid"} +diff --git a/tests/expected/libsmartcols/fromfile b/tests/expected/libsmartcols/fromfile +new file mode 100644 +index 000000000..4155aa36f +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile +@@ -0,0 +1 @@ ++...done. +diff --git a/tests/expected/libsmartcols/fromfile-column-separator b/tests/expected/libsmartcols/fromfile-column-separator +new file mode 100644 +index 000000000..8c5aa2aff +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-column-separator +@@ -0,0 +1,11 @@ ++NAME | NUM|TRUNC ++aaaa | 0|qqqqqqqqqqqqqqqqqX ++bbb | 100|dddddddddddddX ++ccccc | 21|ffffffffffffffffffffffffffffffffffffffffX ++dddddd| 3|ssssssssssX ++ee | 411|ddddddddddddddddddddddddddX ++ffff | 5111|jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX ++gggggg|678993321|mmmmmmmmmmmmmmmmmmmX ++hhh | 7666666|lllllllllllllllllllllllllllllllllllllX ++iiiiii| 8765|yyyyyyyyyyyyyyyyyyyyyyyyyyyyX ++jj | 987456|pppppppppX +diff --git a/tests/expected/libsmartcols/fromfile-export b/tests/expected/libsmartcols/fromfile-export +new file mode 100644 +index 000000000..f63afd882 +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-export +@@ -0,0 +1,10 @@ ++NAME="aaaa" NUM="0" TRUNC="qqqqqqqqqqqqqqqqqX" ++NAME="bbb" NUM="100" TRUNC="dddddddddddddX" ++NAME="ccccc" NUM="21" TRUNC="ffffffffffffffffffffffffffffffffffffffffX" ++NAME="dddddd" NUM="3" TRUNC="ssssssssssX" ++NAME="ee" NUM="411" TRUNC="ddddddddddddddddddddddddddX" ++NAME="ffff" NUM="5111" TRUNC="jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX" ++NAME="gggggg" NUM="678993321" TRUNC="mmmmmmmmmmmmmmmmmmmX" ++NAME="hhh" NUM="7666666" TRUNC="lllllllllllllllllllllllllllllllllllllX" ++NAME="iiiiii" NUM="8765" TRUNC="yyyyyyyyyyyyyyyyyyyyyyyyyyyyX" ++NAME="jj" NUM="987456" TRUNC="pppppppppX" +diff --git a/tests/expected/libsmartcols/fromfile-hidden b/tests/expected/libsmartcols/fromfile-hidden +new file mode 100644 +index 000000000..cf50bcb3e +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-hidden +@@ -0,0 +1,11 @@ ++NAME NUM ++aaaa 0 ++bbb 100 ++ccccc 21 ++dddddd 3 ++ee 411 ++ffff 5111 ++gggggg 678993321 ++hhh 7666666 ++iiiiii 8765 ++jj 987456 +diff --git a/tests/expected/libsmartcols/fromfile-noextremes b/tests/expected/libsmartcols/fromfile-noextremes +new file mode 100644 +index 000000000..4bac08070 +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-noextremes +@@ -0,0 +1,12 @@ ++NAME NOEXTREME NUM NAME NUM ++aaaa qqqqqqX 0 aaaa 0 ++bbb ddddddddX 100 bbb 100 ++ccccc ffffffffffffffffffffffffffffffffffX ++ 21 ccccc 21 ++dddddd sssX 3 dddddd 3 ++ee ddX 411 ee 411 ++ffff jjjjjX 5111 ffff 5111 ++gggggg mmmmmmmX 678993321 gggggg 678993321 ++hhh llllllllllX 7666666 hhh 7666666 ++iiiiii yyyyyyX 8765 iiiiii 8765 ++jj pppppX 987456 jj 987456 +diff --git a/tests/expected/libsmartcols/fromfile-raw b/tests/expected/libsmartcols/fromfile-raw +new file mode 100644 +index 000000000..cc188aaab +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-raw +@@ -0,0 +1,11 @@ ++NAME NUM TRUNC ++aaaa 0 qqqqqqqqqqqqqqqqqX ++bbb 100 dddddddddddddX ++ccccc 21 ffffffffffffffffffffffffffffffffffffffffX ++dddddd 3 ssssssssssX ++ee 411 ddddddddddddddddddddddddddX ++ffff 5111 jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX ++gggggg 678993321 mmmmmmmmmmmmmmmmmmmX ++hhh 7666666 lllllllllllllllllllllllllllllllllllllX ++iiiiii 8765 yyyyyyyyyyyyyyyyyyyyyyyyyyyyX ++jj 987456 pppppppppX +diff --git a/tests/expected/libsmartcols/fromfile-right b/tests/expected/libsmartcols/fromfile-right +new file mode 100644 +index 000000000..d78285a13 +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-right +@@ -0,0 +1,11 @@ ++NAME NUM STRINGS ++aaaa 0 qqqqqqqqqqqqqqqqqX ++bbb 100 dddddddddddddX ++ccccc 21 ffffffffffffffffffffffffffffffffffffffffX ++dddddd 3 ssssssssssX ++ee 411 ddddddddddddddddddddddddddX ++ffff 5111 jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX ++gggggg 678993321 mmmmmmmmmmmmmmmmmmmX ++hhh 7666666 lllllllllllllllllllllllllllllllllllllX ++iiiiii 8765 yyyyyyyyyyyyyyyyyyyyyyyyyyyyX ++jj 987456 pppppppppX +diff --git a/tests/expected/libsmartcols/fromfile-right-maxout b/tests/expected/libsmartcols/fromfile-right-maxout +new file mode 100644 +index 000000000..3bcb65abd +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-right-maxout +@@ -0,0 +1,11 @@ ++NAME NUM ++aaaa 0 ++bbb 100 ++ccccc 21 ++dddddd 3 ++ee 411 ++ffff 5111 ++gggggg 678993321 ++hhh 7666666 ++iiiiii 8765 ++jj 987456 +diff --git a/tests/expected/libsmartcols/fromfile-strictwidth b/tests/expected/libsmartcols/fromfile-strictwidth +new file mode 100644 +index 000000000..41054b235 +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-strictwidth +@@ -0,0 +1,11 @@ ++NAME STRICT NUM ++aaaa 0 0 ++bbb 1 100 ++ccccc 2 21 ++dddddd 3 3 ++ee 4 411 ++ffff 5 5111 ++gggggg 6 678993321 ++hhh 7 7666666 ++iiiiii 8 8765 ++jj 9 987456 +diff --git a/tests/expected/libsmartcols/fromfile-tree b/tests/expected/libsmartcols/fromfile-tree +new file mode 100644 +index 000000000..9d345f8dc +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-tree +@@ -0,0 +1,11 @@ ++TREE ID PARENT STRINGS ++aaaa 1 0 qqqqqqqqqqqqqqqqqX ++|-bbb 2 1 dddddddddddddX ++| |-ee 5 2 ddddddddddddddddddddddddddX ++| `-ffff 6 2 jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX ++|-ccccc 3 1 ffffffffffffffffffffffffffffffffffffffffX ++| `-gggggg 7 3 mmmmmmmmmmmmmmmmmmmX ++| |-hhh 8 7 lllllllllllllllllllllllllllllllllllllX ++| | `-iiiiii 9 8 yyyyyyyyyyyyyyyyyyyyyyyyyyyyX ++| `-jj 10 7 pppppppppX ++`-dddddd 4 1 ssssssssssX +diff --git a/tests/expected/libsmartcols/fromfile-tree-end b/tests/expected/libsmartcols/fromfile-tree-end +new file mode 100644 +index 000000000..41aebff56 +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-tree-end +@@ -0,0 +1,11 @@ ++ID PARENT STRINGS TREE ++ 1 0 qqqqqqqqqqqqqqqqqX aaaa ++ 2 1 dddddddddddddX |-bbb ++ 5 2 ddddddddddddddddddddddddddX | |-ee ++ 6 2 jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX | `-ffff ++ 3 1 ffffffffffffffffffffffffffffffffffffffffX |-ccccc ++ 7 3 mmmmmmmmmmmmmmmmmmmX | `-gggggg ++ 8 7 lllllllllllllllllllllllllllllllllllllX | |-hhh ++ 9 8 yyyyyyyyyyyyyyyyyyyyyyyyyyyyX | | `-iiiiii ++10 7 pppppppppX | `-jj ++ 4 1 ssssssssssX `-dddddd +diff --git a/tests/expected/libsmartcols/fromfile-tree-json b/tests/expected/libsmartcols/fromfile-tree-json +new file mode 100644 +index 000000000..5b3546d0a +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-tree-json +@@ -0,0 +1,29 @@ ++{ ++ "testtable": [ ++ {"tree":"aaaa", "id":"1", "parent":"0", "strings":"qqqqqqqqqqqqqqqqqX", ++ "children": [ ++ {"tree":"bbb", "id":"2", "parent":"1", "strings":"dddddddddddddX", ++ "children": [ ++ {"tree":"ee", "id":"5", "parent":"2", "strings":"ddddddddddddddddddddddddddX"}, ++ {"tree":"ffff", "id":"6", "parent":"2", "strings":"jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX"} ++ ] ++ }, ++ {"tree":"ccccc", "id":"3", "parent":"1", "strings":"ffffffffffffffffffffffffffffffffffffffffX", ++ "children": [ ++ {"tree":"gggggg", "id":"7", "parent":"3", "strings":"mmmmmmmmmmmmmmmmmmmX", ++ "children": [ ++ {"tree":"hhh", "id":"8", "parent":"7", "strings":"lllllllllllllllllllllllllllllllllllllX", ++ "children": [ ++ {"tree":"iiiiii", "id":"9", "parent":"8", "strings":"yyyyyyyyyyyyyyyyyyyyyyyyyyyyX"} ++ ] ++ }, ++ {"tree":"jj", "id":"10", "parent":"7", "strings":"pppppppppX"} ++ ] ++ } ++ ] ++ }, ++ {"tree":"dddddd", "id":"4", "parent":"1", "strings":"ssssssssssX"} ++ ] ++ } ++ ] ++} +diff --git a/tests/expected/libsmartcols/fromfile-tree-middle b/tests/expected/libsmartcols/fromfile-tree-middle +new file mode 100644 +index 000000000..b2183313c +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-tree-middle +@@ -0,0 +1,11 @@ ++ID PARENT TREE STRINGS ++ 1 0 aaaa qqqqqqqqqqqqqqqqqX ++ 2 1 |-bbb dddddddddddddX ++ 5 2 | |-ee ddddddddddddddddddddddddddX ++ 6 2 | `-ffff jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX ++ 3 1 |-ccccc ffffffffffffffffffffffffffffffffffffffffX ++ 7 3 | `-gggggg mmmmmmmmmmmmmmmmmmmX ++ 8 7 | |-hhh lllllllllllllllllllllllllllllllllllllX ++ 9 8 | | `-iiiiii yyyyyyyyyyyyyyyyyyyyyyyyyyyyX ++10 7 | `-jj pppppppppX ++ 4 1 `-dddddd ssssssssssX +diff --git a/tests/expected/libsmartcols/fromfile-trunc b/tests/expected/libsmartcols/fromfile-trunc +new file mode 100644 +index 000000000..6a095dc88 +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-trunc +@@ -0,0 +1,11 @@ ++NAME NUM TRUNC ++aaaa 0 qqqqqqqqqqqqqqqqqX ++bbb 100 dddddddddddddX ++ccccc 21 fffffffffffffffffffffff ++dddddd 3 ssssssssssX ++ee 411 ddddddddddddddddddddddd ++ffff 5111 jjjjjjjjjjjjjjjjjjjjjjj ++gggggg 678993321 mmmmmmmmmmmmmmmmmmmX ++hhh 7666666 lllllllllllllllllllllll ++iiiiii 8765 yyyyyyyyyyyyyyyyyyyyyyy ++jj 987456 pppppppppX +diff --git a/tests/expected/libsmartcols/fromfile-wrap b/tests/expected/libsmartcols/fromfile-wrap +new file mode 100644 +index 000000000..bb94f4973 +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-wrap +@@ -0,0 +1,17 @@ ++NAME NUM WRAP ++aaaa 0 qqqqqqqqqqqqqqqqqX ++bbb 100 dddddddddddddX ++ccccc 21 fffffffffffffffffffffff ++ fffffffffffffffffX ++dddddd 3 ssssssssssX ++ee 411 ddddddddddddddddddddddd ++ dddX ++ffff 5111 jjjjjjjjjjjjjjjjjjjjjjj ++ jjjjjjjjjjjjjjjjjjjjjjj ++ jjjX ++gggggg 678993321 mmmmmmmmmmmmmmmmmmmX ++hhh 7666666 lllllllllllllllllllllll ++ llllllllllllllX ++iiiiii 8765 yyyyyyyyyyyyyyyyyyyyyyy ++ yyyyyX ++jj 987456 pppppppppX +diff --git a/tests/expected/libsmartcols/fromfile-wrap-tree b/tests/expected/libsmartcols/fromfile-wrap-tree +new file mode 100644 +index 000000000..a2db7a4ac +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-wrap-tree +@@ -0,0 +1,18 @@ ++TREE ID PARENT WRAP ++aaaa 1 0 qqqqqqqqqqqqqqqqqX ++|-bbb 2 1 dddddddddddddX ++| |-ee 5 2 dddddddddddddddddddd ++| | ddddddX ++| `-ffff 6 2 jjjjjjjjjjjjjjjjjjjj ++| jjjjjjjjjjjjjjjjjjjj ++| jjjjjjjjjX ++|-ccccc 3 1 ffffffffffffffffffff ++| | ffffffffffffffffffff ++| | X ++| `-gggggg 7 3 mmmmmmmmmmmmmmmmmmmX ++| |-hhh 8 7 llllllllllllllllllll ++| | | lllllllllllllllllX ++| | `-iiiiii 9 8 yyyyyyyyyyyyyyyyyyyy ++| | yyyyyyyyX ++| `-jj 10 7 pppppppppX ++`-dddddd 4 1 ssssssssssX +diff --git a/tests/expected/libsmartcols/fromfile-wrapnl b/tests/expected/libsmartcols/fromfile-wrapnl +new file mode 100644 +index 000000000..c747ebb08 +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-wrapnl +@@ -0,0 +1,19 @@ ++NAME NUM WRAPNL ++aaaa 0 aaa ++bbb 100 bbbbb ++ccccc 21 cccc ++ CCCC ++dddddd 3 dddddddd ++ DDDD ++ DD ++ee 411 hello ++ baby ++ffff 5111 aaa ++ bbb ++ ccc ++ ddd ++gggggg 678993321 eee ++hhh 7666666 fffff ++iiiiii 8765 g ++ hhhhh ++jj 987456 ppppppppp +diff --git a/tests/expected/libsmartcols/fromfile-wrapnl-tree b/tests/expected/libsmartcols/fromfile-wrapnl-tree +new file mode 100644 +index 000000000..3862cf16a +--- /dev/null ++++ b/tests/expected/libsmartcols/fromfile-wrapnl-tree +@@ -0,0 +1,19 @@ ++TREE ID PARENT WRAPNL ++aaaa 1 0 aaa ++|-bbb 2 1 bbbbb ++| |-ee 5 2 hello ++| | baby ++| `-ffff 6 2 aaa ++| bbb ++| ccc ++| ddd ++|-ccccc 3 1 cccc ++| | CCCC ++| `-gggggg 7 3 eee ++| |-hhh 8 7 fffff ++| | `-iiiiii 9 8 g ++| | hhhhh ++| `-jj 10 7 ppppppppp ++`-dddddd 4 1 dddddddd ++ DDDD ++ DD +diff --git a/tests/expected/libsmartcols/title b/tests/expected/libsmartcols/title +new file mode 100644 +index 000000000..bb5d7225a +--- /dev/null ++++ b/tests/expected/libsmartcols/title +@@ -0,0 +1,16 @@ ++ This is right title ++NAME DATA ++foo bla bla bla ++bar alb alb alb ++This is left title (without padding) ++NAME DATA ++foo bla bla bla ++bar alb alb alb ++=======================This is center title (with padding)====================== ++NAME DATA ++foo bla bla bla ++bar alb alb alb ++This is left title (with padding)----------------------------------------------- ++NAME DATA ++foo bla bla bla ++bar alb alb alb +diff --git a/tests/ts/libsmartcols/files/col-hidden b/tests/ts/libsmartcols/files/col-hidden +new file mode 100644 +index 000000000..83182a8ee +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-hidden +@@ -0,0 +1,3 @@ ++FOO ++0 ++hidden +diff --git a/tests/ts/libsmartcols/files/col-id b/tests/ts/libsmartcols/files/col-id +new file mode 100644 +index 000000000..0188f42a0 +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-id +@@ -0,0 +1,3 @@ ++ID ++0 ++right +diff --git a/tests/ts/libsmartcols/files/col-name b/tests/ts/libsmartcols/files/col-name +new file mode 100644 +index 000000000..0a98f29cf +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-name +@@ -0,0 +1,3 @@ ++NAME ++0 ++none +diff --git a/tests/ts/libsmartcols/files/col-noextremes b/tests/ts/libsmartcols/files/col-noextremes +new file mode 100644 +index 000000000..715edce7e +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-noextremes +@@ -0,0 +1,3 @@ ++NOEXTREME ++0 ++noextremes +diff --git a/tests/ts/libsmartcols/files/col-number b/tests/ts/libsmartcols/files/col-number +new file mode 100644 +index 000000000..34a70e4a4 +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-number +@@ -0,0 +1,3 @@ ++NUM ++0 ++right +diff --git a/tests/ts/libsmartcols/files/col-parent b/tests/ts/libsmartcols/files/col-parent +new file mode 100644 +index 000000000..86fe08ced +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-parent +@@ -0,0 +1,3 @@ ++PARENT ++0 ++right +diff --git a/tests/ts/libsmartcols/files/col-strict b/tests/ts/libsmartcols/files/col-strict +new file mode 100644 +index 000000000..62bb96b9a +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-strict +@@ -0,0 +1,3 @@ ++STRICT ++20 ++strictwidth,right +diff --git a/tests/ts/libsmartcols/files/col-string b/tests/ts/libsmartcols/files/col-string +new file mode 100644 +index 000000000..7e2904b9f +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-string +@@ -0,0 +1,3 @@ ++STRINGS ++0 ++none +diff --git a/tests/ts/libsmartcols/files/col-tree b/tests/ts/libsmartcols/files/col-tree +new file mode 100644 +index 000000000..507688000 +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-tree +@@ -0,0 +1,3 @@ ++TREE ++0 ++tree +diff --git a/tests/ts/libsmartcols/files/col-trunc b/tests/ts/libsmartcols/files/col-trunc +new file mode 100644 +index 000000000..2887b4314 +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-trunc +@@ -0,0 +1,3 @@ ++TRUNC ++0 ++trunc +diff --git a/tests/ts/libsmartcols/files/col-wrap b/tests/ts/libsmartcols/files/col-wrap +new file mode 100644 +index 000000000..dc4ca340e +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-wrap +@@ -0,0 +1,3 @@ ++WRAP ++0 ++wrap +diff --git a/tests/ts/libsmartcols/files/col-wrapnl b/tests/ts/libsmartcols/files/col-wrapnl +new file mode 100644 +index 000000000..0a18fd146 +--- /dev/null ++++ b/tests/ts/libsmartcols/files/col-wrapnl +@@ -0,0 +1,3 @@ ++WRAPNL ++0 ++wrapnl +diff --git a/tests/ts/libsmartcols/files/data-id b/tests/ts/libsmartcols/files/data-id +new file mode 100644 +index 000000000..f00c965d8 +--- /dev/null ++++ b/tests/ts/libsmartcols/files/data-id +@@ -0,0 +1,10 @@ ++1 ++2 ++3 ++4 ++5 ++6 ++7 ++8 ++9 ++10 +diff --git a/tests/ts/libsmartcols/files/data-number b/tests/ts/libsmartcols/files/data-number +new file mode 100644 +index 000000000..562d75061 +--- /dev/null ++++ b/tests/ts/libsmartcols/files/data-number +@@ -0,0 +1,10 @@ ++0 ++100 ++21 ++3 ++411 ++5111 ++678993321 ++7666666 ++8765 ++987456 +diff --git a/tests/ts/libsmartcols/files/data-number-tiny b/tests/ts/libsmartcols/files/data-number-tiny +new file mode 100644 +index 000000000..8b1acc12b +--- /dev/null ++++ b/tests/ts/libsmartcols/files/data-number-tiny +@@ -0,0 +1,10 @@ ++0 ++1 ++2 ++3 ++4 ++5 ++6 ++7 ++8 ++9 +diff --git a/tests/ts/libsmartcols/files/data-parent b/tests/ts/libsmartcols/files/data-parent +new file mode 100644 +index 000000000..aa5071608 +--- /dev/null ++++ b/tests/ts/libsmartcols/files/data-parent +@@ -0,0 +1,10 @@ ++0 ++1 ++1 ++1 ++2 ++2 ++3 ++7 ++8 ++7 +diff --git a/tests/ts/libsmartcols/files/data-string b/tests/ts/libsmartcols/files/data-string +new file mode 100644 +index 000000000..dff6e9c80 +--- /dev/null ++++ b/tests/ts/libsmartcols/files/data-string +@@ -0,0 +1,10 @@ ++aaaa ++bbb ++ccccc ++dddddd ++ee ++ffff ++gggggg ++hhh ++iiiiii ++jj +diff --git a/tests/ts/libsmartcols/files/data-string-extreme b/tests/ts/libsmartcols/files/data-string-extreme +new file mode 100644 +index 000000000..6fb395d7a +--- /dev/null ++++ b/tests/ts/libsmartcols/files/data-string-extreme +@@ -0,0 +1,10 @@ ++qqqqqqX ++ddddddddX ++ffffffffffffffffffffffffffffffffffX ++sssX ++ddX ++jjjjjX ++mmmmmmmX ++llllllllllX ++yyyyyyX ++pppppX +diff --git a/tests/ts/libsmartcols/files/data-string-long b/tests/ts/libsmartcols/files/data-string-long +new file mode 100644 +index 000000000..1b5683aa4 +--- /dev/null ++++ b/tests/ts/libsmartcols/files/data-string-long +@@ -0,0 +1,10 @@ ++qqqqqqqqqqqqqqqqqX ++dddddddddddddX ++ffffffffffffffffffffffffffffffffffffffffX ++ssssssssssX ++ddddddddddddddddddddddddddX ++jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX ++mmmmmmmmmmmmmmmmmmmX ++lllllllllllllllllllllllllllllllllllllX ++yyyyyyyyyyyyyyyyyyyyyyyyyyyyX ++pppppppppX +diff --git a/tests/ts/libsmartcols/files/data-string-nl b/tests/ts/libsmartcols/files/data-string-nl +new file mode 100644 +index 000000000..7822e57bc +--- /dev/null ++++ b/tests/ts/libsmartcols/files/data-string-nl +@@ -0,0 +1,10 @@ ++aaa ++bbbbb ++cccc\nCCCC ++dddddddd\nDDDD\nDD ++hello\nbaby ++aaa\nbbb\nccc\nddd ++eee ++fffff ++g\nhhhhh ++ppppppppp +diff --git a/tests/ts/libsmartcols/fromfile b/tests/ts/libsmartcols/fromfile +new file mode 100755 +index 000000000..311dd7e1e +--- /dev/null ++++ b/tests/ts/libsmartcols/fromfile +@@ -0,0 +1,240 @@ ++#!/bin/bash ++# ++# This file is part of util-linux. ++# ++# This file 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 file 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. ++# ++# ++ ++TS_TOPDIR="${0%/*}/../.." ++TS_DESC="fromfile" ++ ++. $TS_TOPDIR/functions.sh ++ts_init "$*" ++ ++TESTPROG="$TS_HELPER_LIBSMARTCOLS_FROMFILE" ++ts_check_test_command "$TESTPROG" ++ ++ts_init_subtest "tree" ++$TESTPROG --nlines 10 \ ++ --tree-id-column 1 \ ++ --tree-parent-column 2 \ ++ --column $TS_SELF/files/col-tree \ ++ --column $TS_SELF/files/col-id \ ++ --column $TS_SELF/files/col-parent \ ++ --column $TS_SELF/files/col-string \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-id \ ++ $TS_SELF/files/data-parent \ ++ $TS_SELF/files/data-string-long \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "tree-json" ++$TESTPROG --nlines 10 --json \ ++ --tree-id-column 1 \ ++ --tree-parent-column 2 \ ++ --column $TS_SELF/files/col-tree \ ++ --column $TS_SELF/files/col-id \ ++ --column $TS_SELF/files/col-parent \ ++ --column $TS_SELF/files/col-string \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-id \ ++ $TS_SELF/files/data-parent \ ++ $TS_SELF/files/data-string-long \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "tree-middle" ++$TESTPROG --nlines 10 \ ++ --tree-id-column 0 \ ++ --tree-parent-column 1 \ ++ --column $TS_SELF/files/col-id \ ++ --column $TS_SELF/files/col-parent \ ++ --column $TS_SELF/files/col-tree \ ++ --column $TS_SELF/files/col-string \ ++ $TS_SELF/files/data-id \ ++ $TS_SELF/files/data-parent \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-string-long \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "tree-end" ++$TESTPROG --nlines 10 \ ++ --tree-id-column 0 \ ++ --tree-parent-column 1 \ ++ --column $TS_SELF/files/col-id \ ++ --column $TS_SELF/files/col-parent \ ++ --column $TS_SELF/files/col-string \ ++ --column $TS_SELF/files/col-tree \ ++ $TS_SELF/files/data-id \ ++ $TS_SELF/files/data-parent \ ++ $TS_SELF/files/data-string-long \ ++ $TS_SELF/files/data-string \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "trunc" ++$TESTPROG --nlines 10 --width 40 \ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-number \ ++ --column $TS_SELF/files/col-trunc \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-number \ ++ $TS_SELF/files/data-string-long \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "right" ++$TESTPROG --nlines 10 \ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-number \ ++ --column $TS_SELF/files/col-string \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-number \ ++ $TS_SELF/files/data-string-long \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "right-maxout" ++$TESTPROG --nlines 10 --maxout --width 80\ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-number \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-number \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "strictwidth" ++$TESTPROG --nlines 10 \ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-strict \ ++ --column $TS_SELF/files/col-number \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-number-tiny \ ++ $TS_SELF/files/data-number \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "noextremes" ++$TESTPROG --nlines 10 --width 45 \ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-noextremes \ ++ --column $TS_SELF/files/col-number \ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-number \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-string-extreme \ ++ $TS_SELF/files/data-number \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-number \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "hidden" ++$TESTPROG --nlines 10 \ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-hidden \ ++ --column $TS_SELF/files/col-number \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-string-long \ ++ $TS_SELF/files/data-number \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "wrap" ++$TESTPROG --nlines 10 --width 40 \ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-number \ ++ --column $TS_SELF/files/col-wrap \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-number \ ++ $TS_SELF/files/data-string-long \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "wrap-tree" ++$TESTPROG --nlines 10 --width 45 \ ++ --tree-id-column 1 \ ++ --tree-parent-column 2 \ ++ --column $TS_SELF/files/col-tree \ ++ --column $TS_SELF/files/col-id \ ++ --column $TS_SELF/files/col-parent \ ++ --column $TS_SELF/files/col-wrap \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-id \ ++ $TS_SELF/files/data-parent \ ++ $TS_SELF/files/data-string-long \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "wrapnl" ++$TESTPROG --nlines 10 \ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-number \ ++ --column $TS_SELF/files/col-wrapnl \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-number \ ++ $TS_SELF/files/data-string-nl \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "wrapnl-tree" ++$TESTPROG --nlines 10 \ ++ --tree-id-column 1 \ ++ --tree-parent-column 2 \ ++ --column $TS_SELF/files/col-tree \ ++ --column $TS_SELF/files/col-id \ ++ --column $TS_SELF/files/col-parent \ ++ --column $TS_SELF/files/col-wrapnl \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-id \ ++ $TS_SELF/files/data-parent \ ++ $TS_SELF/files/data-string-nl \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "raw" ++$TESTPROG --nlines 10 --raw \ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-number \ ++ --column $TS_SELF/files/col-trunc \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-number \ ++ $TS_SELF/files/data-string-long \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "export" ++$TESTPROG --nlines 10 --export \ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-number \ ++ --column $TS_SELF/files/col-trunc \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-number \ ++ $TS_SELF/files/data-string-long \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_init_subtest "column-separator" ++$TESTPROG --nlines 10 --colsep \| \ ++ --column $TS_SELF/files/col-name \ ++ --column $TS_SELF/files/col-number \ ++ --column $TS_SELF/files/col-trunc \ ++ $TS_SELF/files/data-string \ ++ $TS_SELF/files/data-number \ ++ $TS_SELF/files/data-string-long \ ++ >> $TS_OUTPUT 2>&1 ++ts_finalize_subtest ++ ++ts_log "...done." ++ts_finalize +diff --git a/tests/ts/libsmartcols/title b/tests/ts/libsmartcols/title +new file mode 100755 +index 000000000..a9ba6a4fd +--- /dev/null ++++ b/tests/ts/libsmartcols/title +@@ -0,0 +1,28 @@ ++#!/bin/bash ++# ++# This file is part of util-linux. ++# ++# This file 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 file 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. ++# ++# ++ ++TS_TOPDIR="${0%/*}/../.." ++TS_DESC="title" ++ ++. $TS_TOPDIR/functions.sh ++ts_init "$*" ++ ++TESTPROG="$TS_HELPER_LIBSMARTCOLS_TITLE" ++ts_check_test_command "$TESTPROG" ++ ++$TESTPROG --width 80 >> $TS_OUTPUT 2>&1 ++ ++ts_finalize +-- +2.14.4 + diff --git a/SOURCES/0155-lslogins-fix-password-verification.patch b/SOURCES/0155-lslogins-fix-password-verification.patch new file mode 100644 index 0000000..b508526 --- /dev/null +++ b/SOURCES/0155-lslogins-fix-password-verification.patch @@ -0,0 +1,114 @@ +From f769cb435c4db2e7f6d11e14fe87a1c81e0912fe Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 23 May 2018 12:43:26 +0200 +Subject: [PATCH 155/173] lslogins: fix password verification + +Let's follow the standard $id$salt$encrypted password format in +verification code. + +The current code is useless and for example PWD-LOCK column is always +FALSE. + +Upstream: http://github.com/karelzak/util-linux/commit/214fbec40abf0432b8e7968f05024ee76d11b3c7 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1581611 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + login-utils/lslogins.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 74 insertions(+), 4 deletions(-) + +diff --git a/login-utils/lslogins.c b/login-utils/lslogins.c +index d7a24b1fb..041053625 100644 +--- a/login-utils/lslogins.c ++++ b/login-utils/lslogins.c +@@ -541,14 +541,84 @@ static int get_nprocs(const uid_t uid) + return nprocs; + } + ++static const char *get_pwd_method(const char *str, const char **next, unsigned int *sz) ++{ ++ const char *p = str; ++ const char *res = NULL; ++ ++ if (!p || *p++ != '$') ++ return NULL; ++ ++ if (sz) ++ *sz = 0; ++ ++ switch (*p) { ++ case '1': ++ res = "MD5"; ++ if (sz) ++ *sz = 22; ++ break; ++ case '2': ++ p++; ++ if (*p == 'a' || *p == 'y') ++ res = "Blowfish"; ++ break; ++ case '5': ++ res = "SHA-256"; ++ if (sz) ++ *sz = 43; ++ break; ++ case '6': ++ res = "SHA-512"; ++ if (sz) ++ *sz = 86; ++ break; ++ default: ++ return NULL; ++ } ++ p++; ++ ++ if (!*p || *p != '$') ++ return NULL; ++ if (next) ++ *next = ++p; ++ return res; ++} ++ ++#define is_valid_pwd_char(x) (isalnum((unsigned char) (x)) || (x) == '.' || (x) == '/') ++ + static int valid_pwd(const char *str) + { +- const char *p; ++ const char *p = str; ++ unsigned int sz = 0, n; ++ ++ /* $id$ */ ++ if (get_pwd_method(str, &p, &sz) == NULL) ++ return 0; ++ if (!*p) ++ return 0; + +- for (p = str; p && *p; p++) +- if (!isalnum((unsigned int) *p)) ++ /* salt$ */ ++ for (; p && *p; p++) { ++ if (*p == '$') { ++ p++; ++ break; ++ } ++ if (!is_valid_pwd_char(*p)) + return 0; +- return p > str ? 1 : 0; ++ } ++ if (!*p) ++ return 0; ++ ++ /* encrypted */ ++ for (n = 0; p && *p; p++, n++) { ++ if (!is_valid_pwd_char(*p)) ++ return 0; ++ } ++ ++ if (sz && n != sz) ++ return 0; ++ return 1; + } + + static struct lslogins_user *get_user_info(struct lslogins_control *ctl, const char *username) +-- +2.14.4 + diff --git a/SOURCES/0156-umount-add-note-about-lazy.patch b/SOURCES/0156-umount-add-note-about-lazy.patch new file mode 100644 index 0000000..fc01a26 --- /dev/null +++ b/SOURCES/0156-umount-add-note-about-lazy.patch @@ -0,0 +1,53 @@ +From 0ae2aa6a43a852cb0326faade1532c02aa7617a4 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 20 Apr 2018 09:50:04 +0200 +Subject: [PATCH 156/173] umount: add note about --lazy + +Unfortunately, it's pretty common that users on production systems use +lazy umount to fix some FS issues. The usual result is unwanted system +reboot, because -l is not the right way how to fix unreachable NFS or +mess with local FS with submounts. + +Note that after lazy umount /proc/self/mountinfo does not contain the +FS entry, but kernel still references the FS. It makes it very +difficult to debug. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1566674 +Suggested-by: Steve Dickson <steved@redhat.com> +Signed-off-by: Karel Zak <kzak@redhat.com> +Upstream: http://github.com/karelzak/util-linux/commit/031800ff6c66b4d62229cfd116195950a718a21c +--- + sys-utils/umount.8 | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/sys-utils/umount.8 b/sys-utils/umount.8 +index f0a712b06..71c2155b4 100644 +--- a/sys-utils/umount.8 ++++ b/sys-utils/umount.8 +@@ -54,7 +54,8 @@ working directory there, or when a swap file on it is in use. The + offending process could even be + .B umount + itself - it opens libc, and libc in its turn may open for example locale +-files. A lazy unmount avoids this problem. ++files. A lazy unmount avoids this problem, but it may introduce another ++issues. See \fB\-\-lazy\fR description bellow. + .SH OPTIONS + .TP + \fB\-a\fR, \fB\-\-all\fR +@@ -107,6 +108,13 @@ Unmount without writing in + Lazy unmount. Detach the filesystem from the filesystem hierarchy now, + and cleanup all references to the filesystem as soon as it is not busy + anymore. (Requires kernel 2.4.11 or later.) ++ ++A system reboot would be expected in near future if you're going to use this ++option for network filesystem or local filesystem with submounts. The ++recommended use-case for \fBumount -l\fR is to prevent hangs on shutdown due to ++an unreachable network share where a normal umount will hang due to a downed ++server or a network partition. Remounts of the share will not be possible. ++ + .TP + \fB\-O\fR, \fB\-\-test\-opts\fR \fIoptions,list\fR + Indicate that the actions should only be taken on file systems with the +-- +2.14.4 + diff --git a/SOURCES/0157-losetup-add-info-about-lazy-detach-to-manpage.patch b/SOURCES/0157-losetup-add-info-about-lazy-detach-to-manpage.patch new file mode 100644 index 0000000..cd95dfe --- /dev/null +++ b/SOURCES/0157-losetup-add-info-about-lazy-detach-to-manpage.patch @@ -0,0 +1,31 @@ +From 0258872e2b45b589481e6796aa983f8749be9ada Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 10 Aug 2017 12:37:34 +0200 +Subject: [PATCH 157/173] losetup: add info about lazy detach to manpage + +Upstream: http://github.com/karelzak/util-linux/commit/60eedb0a53f14aa1169713a72325421db8e1c647 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1566390 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/losetup.8 | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/sys-utils/losetup.8 b/sys-utils/losetup.8 +index 9a8c1d597..45c220187 100644 +--- a/sys-utils/losetup.8 ++++ b/sys-utils/losetup.8 +@@ -80,7 +80,10 @@ without --list) is deprecated. + .IP "\fB\-c, \-\-set-capacity\fP \fIloopdev\fP + force loop driver to reread size of the file associated with the specified loop device + .IP "\fB\-d, \-\-detach\fP \fIloopdev\fP..." +-detach the file or device associated with the specified loop device(s) ++detach the file or device associated with the specified loop device(s). Note ++that since Linux v3.7 kernel uses "lazy device destruction". The detach ++operation does not return EBUSY error anymore if device is actively used by ++system, but it is marked by autoclear flag and destroyed later. + .IP "\fB\-D, \-\-detach-all\fP" + detach all associated loop devices + .IP "\fB\-f, \-\-find\fP" +-- +2.14.4 + diff --git a/SOURCES/0158-setarch-Fix-ppc64le-architectures.patch b/SOURCES/0158-setarch-Fix-ppc64le-architectures.patch new file mode 100644 index 0000000..6e0b02d --- /dev/null +++ b/SOURCES/0158-setarch-Fix-ppc64le-architectures.patch @@ -0,0 +1,44 @@ +From e10a3a201b2b4dc6ff9957600c9bf67a2f9de38e Mon Sep 17 00:00:00 2001 +From: Anton Blanchard <anton@samba.org> +Date: Mon, 7 Apr 2014 09:18:13 +1000 +Subject: [PATCH 158/173] setarch: Fix ppc64le architectures + +setarch currently fails on ppc64le because it tries to +use big endian architecture names. Fix it. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1562125 +Upstream: http://github.com/karelzak/util-linux/commit/95bf26fd68ec7f0b2dde1f022dc79d04d1a6e620 +Upstream: http://github.com/karelzak/util-linux/commit/e60b6df54e27cdb68a75ea8a056a70f60df8f025 +Signed-off-by: Anton Blanchard <anton@samba.org> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/setarch.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/sys-utils/setarch.c b/sys-utils/setarch.c +index 051cbefcd..b03406dd3 100644 +--- a/sys-utils/setarch.c ++++ b/sys-utils/setarch.c +@@ -149,11 +149,19 @@ set_arch(const char *pers, unsigned long options, int list) + {PER_LINUX32, "linux32", NULL}, + {PER_LINUX, "linux64", NULL}, + #if defined(__powerpc__) || defined(__powerpc64__) ++#ifdef __BIG_ENDIAN__ + {PER_LINUX32, "ppc32", "ppc"}, + {PER_LINUX32, "ppc", "ppc"}, + {PER_LINUX, "ppc64", "ppc64"}, + {PER_LINUX, "ppc64pseries", "ppc64"}, + {PER_LINUX, "ppc64iseries", "ppc64"}, ++#else ++ {PER_LINUX32, "ppc32", "ppcle"}, ++ {PER_LINUX32, "ppc", "ppcle"}, ++ {PER_LINUX32, "ppc32le", "ppcle"}, ++ {PER_LINUX32, "ppcle", "ppcle"}, ++ {PER_LINUX, "ppc64le", "ppc64le"}, ++#endif + #endif + #if defined(__x86_64__) || defined(__i386__) || defined(__ia64__) + {PER_LINUX32, "i386", "i386"}, +-- +2.14.4 + diff --git a/SOURCES/0159-fallocate-backport-v2.32-164-g641af90dc.patch b/SOURCES/0159-fallocate-backport-v2.32-164-g641af90dc.patch new file mode 100644 index 0000000..615638d --- /dev/null +++ b/SOURCES/0159-fallocate-backport-v2.32-164-g641af90dc.patch @@ -0,0 +1,717 @@ +From 9f2a32a8fd08bb1b48f29c88bfa398fa4eb5f2a4 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 6 Jun 2018 11:59:16 +0200 +Subject: [PATCH 159/173] fallocate: backport v2.32-164-g641af90dc + +* add --dig-holes +* add --collapse-range +* add --insert-range +* add --zero-range + +For backward compatibility with previous RHEL7 versions we keep +O_CREAT for open(). The current upstream uses O_CREAT only when +necessary. + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1528567 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/fallocate.1 | 195 ++++++++++++++++++++++------ + sys-utils/fallocate.c | 349 +++++++++++++++++++++++++++++++++++++++----------- + 2 files changed, 428 insertions(+), 116 deletions(-) + +diff --git a/sys-utils/fallocate.1 b/sys-utils/fallocate.1 +index 376353013..d4821dcd1 100644 +--- a/sys-utils/fallocate.1 ++++ b/sys-utils/fallocate.1 +@@ -1,72 +1,185 @@ +-.\" -*- nroff -*- +-.TH FALLOCATE 1 "September 2011" "util-linux" "User Commands" ++.TH FALLOCATE 1 "April 2014" "util-linux" "User Commands" + .SH NAME +-fallocate \- preallocate space to a file ++fallocate \- preallocate or deallocate space to a file + .SH SYNOPSIS + .B fallocate +-.RB [ \-n ] +-.RB [ \-p ] ++.RB [ \-c | \-p | \-z ] + .RB [ \-o + .IR offset ] + .B \-l +-.IR length ++.I length ++.RB [ \-n ] ++.I filename ++.PP ++.B fallocate \-d ++.RB [ \-o ++.IR offset ] ++.RB [ \-l ++.IR length ] + .I filename + .PP + .B fallocate \-x + .RB [ \-o + .IR offset ] +-.RB \-l +-.IR length ++.B \-l ++.I length + .I filename + .SH DESCRIPTION + .B fallocate +-is used to preallocate blocks to a file. For filesystems which support the +-fallocate system call, this is done quickly by allocating blocks and marking +-them as uninitialized, requiring no IO to the data blocks. This is much faster +-than creating a file by filling it with zeros. +-.PP +-As of the Linux Kernel v2.6.31, the fallocate system call is supported by the +-btrfs, ext4, ocfs2, and xfs filesystems. ++is used to manipulate the allocated disk space for a file, ++either to deallocate or preallocate it. ++For filesystems which support the fallocate system call, ++preallocation is done quickly by allocating blocks and marking them as ++uninitialized, requiring no IO to the data blocks. ++This is much faster than creating a file by filling it with zeroes. + .PP + The exit code returned by + .B fallocate + is 0 on success and 1 on failure. +-.PP + .SH OPTIONS +-The \fIlength\fR and \fIoffset\fR 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\-n, \-\-keep-size\fP" ++The ++.I length ++and ++.I offset ++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. ++.PP ++The options ++.BR \-\-collapse\-range ", " \-\-dig\-holes ", " \-\-punch\-hole , ++and ++.B \-\-zero\-range ++are mutually exclusive. ++.TP ++.BR \-c ", " \-\-collapse\-range ++Removes a byte range from a file, without leaving a hole. ++The byte range to be collapsed starts at ++.I offset ++and continues for ++.I length ++bytes. ++At the completion of the operation, ++the contents of the file starting at the location ++.IR offset + length ++will be appended at the location ++.IR offset , ++and the file will be ++.I length ++bytes smaller. ++The option ++.B \-\-keep\-size ++may not be specified for the collapse-range operation. ++.sp ++Available since Linux 3.15 for ext4 (only for extent-based files) and XFS. ++.TP ++.BR \-d ", " \-\-dig\-holes ++Detect and dig holes. ++This makes the file sparse in-place, without using extra disk space. ++The minimum size of the hole depends on filesystem I/O block size ++(usually 4096 bytes). ++Also, when using this option, ++.B \-\-keep\-size ++is implied. If no range is specified by ++.B \-\-offset ++and ++.BR \-\-length , ++then the entire file is analyzed for holes. ++.sp ++You can think of this option as doing a ++.RB """" "cp \-\-sparse" """" ++and then renaming the destination file to the original, ++without the need for extra disk space. ++.sp ++See \fB\-\-punch\-hole\fP for a list of supported filesystems. ++.TP ++.BR \-i ", " \-\-insert\-range ++Insert a hole of ++.I length ++bytes from ++.IR offset , ++shifting existing data. ++.TP ++.BR \-l ", " "\-\-length " \fIlength ++Specifies the length of the range, in bytes. ++.TP ++.BR \-n ", " \-\-keep\-size + Do not modify the apparent length of the file. This may effectively allocate + blocks past EOF, which can be removed with a truncate. +-.IP "\fB\-p, \-\-punch-hole\fP" +-Punch holes in the file, the range should not exceed the length of the file. +-.IP "\fB\-o, \-\-offset\fP \fIoffset\fP +-Specifies the beginning offset of the allocation, in bytes. +-.IP "\fB\-l, \-\-length\fP \fIlength\fP +-Specifies the length of the allocation, in bytes. +-.IP "\fB\-x , \-\-posix\fP +-Enable POSIX operation mode. In that mode allocation operation always completes, +-but it may take longer time when fast allocation is not supported by the underlying filesystem. +-.IP "\fB\-h, \-\-help\fP" +-Print help and exit. +-.IP "\fB-V, \-\-version" +-Print version and exit. ++.TP ++.BR \-o ", " "\-\-offset " \fIoffset ++Specifies the beginning offset of the range, in bytes. ++.TP ++.BR \-p ", " \-\-punch\-hole ++Deallocates space (i.e., creates a hole) in the byte range starting at ++.I offset ++and continuing for ++.I length ++bytes. ++Within the specified range, partial filesystem blocks are zeroed, ++and whole filesystem blocks are removed from the file. ++After a successful call, ++subsequent reads from this range will return zeroes. ++This option may not be specified at the same time as the ++.B \-\-zero\-range ++option. ++Also, when using this option, ++.B \-\-keep\-size ++is implied. ++.sp ++Supported for XFS (since Linux 2.6.38), ext4 (since Linux 3.0), ++Btrfs (since Linux 3.7) and tmpfs (since Linux 3.5). ++.TP ++.BR \-v ", " \-\-verbose ++Enable verbose mode. ++.TP ++.BR \-x ", " \-\-posix ++Enable POSIX operation mode. ++In that mode allocation operation always completes, ++but it may take longer time when fast allocation is not supported by ++the underlying filesystem. ++.TP ++.BR \-z ", " \-\-zero\-range ++Zeroes space in the byte range starting at ++.I offset ++and continuing for ++.I length ++bytes. ++Within the specified range, blocks are preallocated for the regions ++that span the holes in the file. ++After a successful call, ++subsequent reads from this range will return zeroes. ++.sp ++Zeroing is done within the filesystem preferably by converting the ++range into unwritten extents. This approach means that the specified ++range will not be physically zeroed out on the device (except for ++partial blocks at the either end of the range), and I/O is ++(otherwise) required only to update metadata. ++.sp ++Option \fB\-\-keep\-size\fP can be specified to prevent file length ++modification. ++.sp ++Available since Linux 3.14 for ext4 (only for extent-based files) and XFS. ++.TP ++.BR \-V ", " \-\-version ++Display version information and exit. ++.TP ++.BR \-h ", " \-\-help ++Display help text and exit. + .SH AUTHORS +-.UR sandeen@redhat.com ++.MT sandeen@redhat.com + Eric Sandeen +-.UE ++.ME + .br +-.UR kzak@redhat.com ++.MT kzak@redhat.com + Karel Zak +-.UE ++.ME + .SH SEE ALSO ++.BR truncate (1), + .BR fallocate (2), +-.BR posix_fallocate (3), +-.BR truncate (1) ++.BR posix_fallocate (3) + .SH AVAILABILITY + The fallocate command is part of the util-linux package and is available from +-.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/ ++.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/ + Linux Kernel Archive + .UE . +diff --git a/sys-utils/fallocate.c b/sys-utils/fallocate.c +index 17ae5fe69..75d89a7a9 100644 +--- a/sys-utils/fallocate.c ++++ b/sys-utils/fallocate.c +@@ -23,6 +23,7 @@ + */ + #include <sys/stat.h> + #include <sys/types.h> ++#include <sys/mman.h> + #include <ctype.h> + #include <errno.h> + #include <fcntl.h> +@@ -31,50 +32,110 @@ + #include <unistd.h> + #include <getopt.h> + #include <limits.h> ++#include <string.h> + + #ifndef HAVE_FALLOCATE + # include <sys/syscall.h> + #endif + +-#ifdef HAVE_LINUX_FALLOC_H +-# include <linux/falloc.h> /* for FALLOC_FL_* flags */ ++#if defined(HAVE_LINUX_FALLOC_H) && \ ++ (!defined(FALLOC_FL_KEEP_SIZE) || !defined(FALLOC_FL_PUNCH_HOLE) || \ ++ !defined(FALLOC_FL_COLLAPSE_RANGE) || !defined(FALLOC_FL_ZERO_RANGE) || \ ++ !defined(FALLOC_FL_INSERT_RANGE)) ++# include <linux/falloc.h> /* non-libc fallback for FALLOC_FL_* flags */ + #endif + ++ + #ifndef FALLOC_FL_KEEP_SIZE +-# define FALLOC_FL_KEEP_SIZE 1 ++# define FALLOC_FL_KEEP_SIZE 0x1 + #endif + + #ifndef FALLOC_FL_PUNCH_HOLE +-# define FALLOC_FL_PUNCH_HOLE 2 ++# define FALLOC_FL_PUNCH_HOLE 0x2 ++#endif ++ ++#ifndef FALLOC_FL_COLLAPSE_RANGE ++# define FALLOC_FL_COLLAPSE_RANGE 0x8 ++#endif ++ ++#ifndef FALLOC_FL_ZERO_RANGE ++# define FALLOC_FL_ZERO_RANGE 0x10 ++#endif ++ ++#ifndef FALLOC_FL_INSERT_RANGE ++# define FALLOC_FL_INSERT_RANGE 0x20 + #endif + + #include "nls.h" + #include "strutils.h" + #include "c.h" + #include "closestream.h" ++#include "xalloc.h" + #include "optutils.h" + +-static void __attribute__((__noreturn__)) usage(FILE *out) ++static int verbose; ++static char *filename; ++ ++static void __attribute__((__noreturn__)) usage(void) + { ++ FILE *out = stdout; + fputs(USAGE_HEADER, out); + fprintf(out, + _(" %s [options] <filename>\n"), program_invocation_short_name); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Preallocate space to, or deallocate space from a file.\n"), out); ++ + fputs(USAGE_OPTIONS, out); +- fputs(_(" -n, --keep-size don't modify the length of the file\n" +- " -p, --punch-hole punch holes in the file\n" +- " -o, --offset <num> offset of the allocation, in bytes\n" +- " -l, --length <num> length of the allocation, in bytes\n"), out); ++ fputs(_(" -c, --collapse-range remove a range from the file\n"), out); ++ fputs(_(" -d, --dig-holes detect zeroes and replace with holes\n"), out); ++ fputs(_(" -i, --insert-range insert a hole at range, shifting existing data\n"), out); ++ fputs(_(" -l, --length <num> length for range operations, in bytes\n"), out); ++ fputs(_(" -n, --keep-size maintain the apparent size of the file\n"), out); ++ fputs(_(" -o, --offset <num> offset for range operations, in bytes\n"), out); ++ fputs(_(" -p, --punch-hole replace a range with a hole (implies -n)\n"), out); ++ fputs(_(" -z, --zero-range zero and ensure allocation of a range\n"), out); + #ifdef HAVE_POSIX_FALLOCATE +- fputs(_(" -x, --posix use posix_fallocate(3) instead of fallocate(2)\n"), out); ++ fputs(_(" -x, --posix use posix_fallocate(3) instead of fallocate(2)\n"), out); + #endif ++ fputs(_(" -v, --verbose verbose mode\n"), out); ++ + fputs(USAGE_SEPARATOR, out); +- fputs(USAGE_HELP, out); +- fputs(USAGE_VERSION, out); +- fprintf(out, USAGE_MAN_TAIL("fallocate(1)")); ++ printf(USAGE_HELP_OPTIONS(22)); ++ ++ printf(USAGE_MAN_TAIL("fallocate(1)")); + +- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); ++ exit(EXIT_SUCCESS); + } + ++static loff_t cvtnum(char *s) ++{ ++ uintmax_t x; ++ ++ if (strtosize(s, &x)) ++ return -1LL; ++ ++ return x; ++} ++ ++static void xfallocate(int fd, int mode, off_t offset, off_t length) ++{ ++ int error; ++#ifdef HAVE_FALLOCATE ++ error = fallocate(fd, mode, offset, length); ++#else ++ error = syscall(SYS_fallocate, fd, mode, offset, length); ++#endif ++ /* ++ * EOPNOTSUPP: The FALLOC_FL_KEEP_SIZE is unsupported ++ * ENOSYS: The filesystem does not support sys_fallocate ++ */ ++ if (error < 0) { ++ if ((mode & FALLOC_FL_KEEP_SIZE) && errno == EOPNOTSUPP) ++ errx(EXIT_FAILURE, _("fallocate failed: keep size mode is unsupported")); ++ err(EXIT_FAILURE, _("fallocate failed")); ++ } ++} + + #ifdef HAVE_POSIX_FALLOCATE + static void xposix_fallocate(int fd, off_t offset, off_t length) +@@ -86,41 +147,163 @@ static void xposix_fallocate(int fd, off_t offset, off_t length) + } + #endif + ++/* The real buffer size has to be bufsize + sizeof(uintptr_t) */ ++static int is_nul(void *buf, size_t bufsize) ++{ ++ typedef uintptr_t word; ++ void const *vp; ++ char const *cbuf = buf, *cp; ++ word const *wp = buf; + +-static loff_t cvtnum(char *s) ++ /* set sentinel */ ++ memset((char *) buf + bufsize, '\1', sizeof(word)); ++ ++ /* Find first nonzero *word*, or the word with the sentinel. */ ++ while (*wp++ == 0) ++ continue; ++ ++ /* Find the first nonzero *byte*, or the sentinel. */ ++ vp = wp - 1; ++ cp = vp; ++ ++ while (*cp++ == 0) ++ continue; ++ ++ return cbuf + bufsize < cp; ++} ++ ++static void dig_holes(int fd, off_t file_off, off_t len) + { +- uintmax_t x; ++ off_t file_end = len ? file_off + len : 0; ++ off_t hole_start = 0, hole_sz = 0; ++ uintmax_t ct = 0; ++ size_t bufsz; ++ char *buf; ++ struct stat st; ++#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE) ++ off_t cache_start = file_off; ++ /* ++ * We don't want to call POSIX_FADV_DONTNEED to discard cached ++ * data in PAGE_SIZE steps. IMHO it's overkill (too many syscalls). ++ * ++ * Let's assume that 1MiB (on system with 4K page size) is just ++ * a good compromise. ++ * -- kzak Feb-2014 ++ */ ++ const size_t cachesz = getpagesize() * 256; ++#endif + +- if (strtosize(s, &x)) +- return -1LL; ++ if (fstat(fd, &st) != 0) ++ err(EXIT_FAILURE, _("stat of %s failed"), filename); + +- return x; ++ bufsz = st.st_blksize; ++ ++ if (lseek(fd, file_off, SEEK_SET) < 0) ++ err(EXIT_FAILURE, _("seek on %s failed"), filename); ++ ++ /* buffer + extra space for is_nul() sentinel */ ++ buf = xmalloc(bufsz + sizeof(uintptr_t)); ++ while (file_end == 0 || file_off < file_end) { ++ /* ++ * Detect data area (skip holes) ++ */ ++ off_t end, off; ++ ++ off = lseek(fd, file_off, SEEK_DATA); ++ if ((off == -1 && errno == ENXIO) || ++ (file_end && off >= file_end)) ++ break; ++ ++ end = lseek(fd, off, SEEK_HOLE); ++ if (file_end && end > file_end) ++ end = file_end; ++ ++#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE) ++ posix_fadvise(fd, off, end, POSIX_FADV_SEQUENTIAL); ++#endif ++ /* ++ * Dig holes in the area ++ */ ++ while (off < end) { ++ ssize_t rsz = pread(fd, buf, bufsz, off); ++ if (rsz < 0 && errno) ++ err(EXIT_FAILURE, _("%s: read failed"), filename); ++ if (end && rsz > 0 && off > end - rsz) ++ rsz = end - off; ++ if (rsz <= 0) ++ break; ++ ++ if (is_nul(buf, rsz)) { ++ if (!hole_sz) /* new hole detected */ ++ hole_start = off; ++ hole_sz += rsz; ++ } else if (hole_sz) { ++ xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, ++ hole_start, hole_sz); ++ ct += hole_sz; ++ hole_sz = hole_start = 0; ++ } ++ ++#if defined(POSIX_FADV_DONTNEED) && defined(HAVE_POSIX_FADVISE) ++ /* discard cached data */ ++ if (off - cache_start > (off_t) cachesz) { ++ size_t clen = off - cache_start; ++ ++ clen = (clen / cachesz) * cachesz; ++ posix_fadvise(fd, cache_start, clen, POSIX_FADV_DONTNEED); ++ cache_start = cache_start + clen; ++ } ++#endif ++ off += rsz; ++ } ++ if (hole_sz) { ++ xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, ++ hole_start, hole_sz); ++ ct += hole_sz; ++ } ++ file_off = off; ++ } ++ ++ free(buf); ++ ++ if (verbose) { ++ char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE, ct); ++ fprintf(stdout, _("%s: %s (%ju bytes) converted to sparse holes.\n"), ++ filename, str, ct); ++ free(str); ++ } + } + + int main(int argc, char **argv) + { +- char *fname; + int c; +- int error = 0; + int fd; + int mode = 0; +- int posix = 0; ++ int dig = 0; ++ int posix = 0; + loff_t length = -2LL; + loff_t offset = 0; + + static const struct option longopts[] = { +- { "help", 0, 0, 'h' }, +- { "version", 0, 0, 'V' }, +- { "keep-size", 0, 0, 'n' }, +- { "punch-hole", 0, 0, 'p' }, +- { "offset", 1, 0, 'o' }, +- { "length", 1, 0, 'l' }, +- { "posix", 0, 0, 'x' }, +- { NULL, 0, 0, 0 } ++ { "help", no_argument, NULL, 'h' }, ++ { "version", no_argument, NULL, 'V' }, ++ { "keep-size", no_argument, NULL, 'n' }, ++ { "punch-hole", no_argument, NULL, 'p' }, ++ { "collapse-range", no_argument, NULL, 'c' }, ++ { "dig-holes", no_argument, NULL, 'd' }, ++ { "insert-range", no_argument, NULL, 'i' }, ++ { "zero-range", no_argument, NULL, 'z' }, ++ { "offset", required_argument, NULL, 'o' }, ++ { "length", required_argument, NULL, 'l' }, ++ { "posix", no_argument, NULL, 'x' }, ++ { "verbose", no_argument, NULL, 'v' }, ++ { NULL, 0, NULL, 0 } + }; + +- static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ +- { 'x', 'n', 'p' }, ++ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ ++ { 'c', 'd', 'p', 'z' }, ++ { 'c', 'n' }, ++ { 'x', 'c', 'd', 'i', 'n', 'p', 'z'}, + { 0 } + }; + int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; +@@ -130,29 +313,39 @@ int main(int argc, char **argv) + textdomain(PACKAGE); + atexit(close_stdout); + +- while ((c = getopt_long(argc, argv, "hVnpl:o:x", longopts, NULL)) != -1) { ++ while ((c = getopt_long(argc, argv, "hvVncpdizxl:o:", longopts, NULL)) ++ != -1) { + + err_exclusive_options(c, longopts, excl, excl_st); + + switch(c) { + case 'h': +- usage(stdout); ++ usage(); + break; +- case 'V': +- printf(UTIL_LINUX_VERSION); +- return EXIT_SUCCESS; +- case 'p': +- mode |= FALLOC_FL_PUNCH_HOLE; +- /* fall through */ +- case 'n': +- mode |= FALLOC_FL_KEEP_SIZE; ++ case 'c': ++ mode |= FALLOC_FL_COLLAPSE_RANGE; ++ break; ++ case 'd': ++ dig = 1; ++ break; ++ case 'i': ++ mode |= FALLOC_FL_INSERT_RANGE; + break; + case 'l': + length = cvtnum(optarg); + break; ++ case 'n': ++ mode |= FALLOC_FL_KEEP_SIZE; ++ break; + case 'o': + offset = cvtnum(optarg); + break; ++ case 'p': ++ mode |= FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE; ++ break; ++ case 'z': ++ mode |= FALLOC_FL_ZERO_RANGE; ++ break; + case 'x': + #ifdef HAVE_POSIX_FALLOCATE + posix = 1; +@@ -160,53 +353,59 @@ int main(int argc, char **argv) + #else + errx(EXIT_FAILURE, _("posix_fallocate support is not compiled")) + #endif +- default: +- usage(stderr); ++ case 'v': ++ verbose++; + break; ++ case 'V': ++ printf(UTIL_LINUX_VERSION); ++ return EXIT_SUCCESS; ++ default: ++ errtryhelp(EXIT_FAILURE); + } + } + +- if (length == -2LL) +- errx(EXIT_FAILURE, _("no length argument specified")); +- if (length <= 0) +- errx(EXIT_FAILURE, _("invalid length value specified")); +- if (offset < 0) +- errx(EXIT_FAILURE, _("invalid offset value specified")); + if (optind == argc) +- errx(EXIT_FAILURE, _("no filename specified.")); ++ errx(EXIT_FAILURE, _("no filename specified")); ++ ++ filename = argv[optind++]; + +- fname = argv[optind++]; ++ if (optind != argc) ++ errx(EXIT_FAILURE, _("unexpected number of arguments")); + +- if (optind != argc) { +- warnx(_("unexpected number of arguments")); +- usage(stderr); ++ if (dig) { ++ /* for --dig-holes the default is analyze all file */ ++ if (length == -2LL) ++ length = 0; ++ if (length < 0) ++ errx(EXIT_FAILURE, _("invalid length value specified")); ++ } else { ++ /* it's safer to require the range specification (--length --offset) */ ++ if (length == -2LL) ++ errx(EXIT_FAILURE, _("no length argument specified")); ++ if (length <= 0) ++ errx(EXIT_FAILURE, _("invalid length value specified")); + } ++ if (offset < 0) ++ errx(EXIT_FAILURE, _("invalid offset value specified")); + +- fd = open(fname, O_WRONLY|O_CREAT, 0644); ++ /* O_CREAT makes sense only for the default fallocate(2) behavior ++ * when mode is no specified and new space is allocated ++ * ++ * RHEL7.6: for backward compatibility in RHEL7 we keep O_CREAT there. ++ */ ++ fd = open(filename, O_RDWR | O_CREAT, ++ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd < 0) +- err(EXIT_FAILURE, _("cannot open %s"), fname); ++ err(EXIT_FAILURE, _("cannot open %s"), filename); + ++ if (dig) ++ dig_holes(fd, offset, length); + #ifdef HAVE_POSIX_FALLOCATE +- if (posix) ++ else if (posix) + xposix_fallocate(fd, offset, length); +- else + #endif +- +-#ifdef HAVE_FALLOCATE +- error = fallocate(fd, mode, offset, length); +-#else +- error = syscall(SYS_fallocate, fd, mode, offset, length); +-#endif +- /* +- * EOPNOTSUPP: The FALLOC_FL_KEEP_SIZE is unsupported +- * ENOSYS: The filesystem does not support sys_fallocate +- */ +- if (error < 0) { +- if ((mode & FALLOC_FL_KEEP_SIZE) && errno == EOPNOTSUPP) +- errx(EXIT_FAILURE, +- _("keep size mode (-n option) unsupported")); +- err(EXIT_FAILURE, _("%s: fallocate failed"), fname); +- } ++ else ++ xfallocate(fd, mode, offset, length); + + close(fd); + return EXIT_SUCCESS; +-- +2.14.4 + diff --git a/SOURCES/0160-libmount-fix-mnt_table_is_fs_mounted-for-rbind.patch b/SOURCES/0160-libmount-fix-mnt_table_is_fs_mounted-for-rbind.patch new file mode 100644 index 0000000..8a66eac --- /dev/null +++ b/SOURCES/0160-libmount-fix-mnt_table_is_fs_mounted-for-rbind.patch @@ -0,0 +1,54 @@ +From 7f5f61824b3b7ffa81d34481aec5a7ecca213a5a Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 3 Jan 2018 13:59:59 +0100 +Subject: [PATCH 160/173] libmount: fix mnt_table_is_fs_mounted() for rbind + +There is no difference between "bind" and "rbind" if we want to FS +root to search for the FS in mountinfo file. + +fstab: + + /dev/sdc1 /mnt/foo xfs defaults 0 0 + /mnt/foo /mnt/test none rw,rbind 0 0 + +use -a more than once: + + mount -a + mount -a + +/proc/mounts (the current result): + + /dev/sdc1 /mnt/foo xfs rw,relatime,attr2,inode64,noquota 0 0 + /dev/sdc1 /mnt/test xfs rw,relatime,attr2,inode64,noquota 0 0 + /dev/sdc1 /mnt/test xfs rw,relatime,attr2,inode64,noquota 0 0 + /dev/sdc1 /mnt/foo xfs rw,relatime,attr2,inode64,noquota 0 0 + +expected (fixed version) result: + + /dev/sdc1 /mnt/foo xfs rw,relatime,attr2,inode64,noquota 0 0 + /dev/sdc1 /mnt/test xfs rw,relatime,attr2,inode64,noquota 0 0 + +Upstream: http://github.com/karelzak/util-linux/commit/b5cc232362a60b2a7738ee250609202bd6195e49 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1528959 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libmount/src/tab.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/libmount/src/tab.c b/libmount/src/tab.c +index 5628da6e5..dfa1da822 100644 +--- a/libmount/src/tab.c ++++ b/libmount/src/tab.c +@@ -1099,7 +1099,8 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) + struct libmnt_fs *rootfs; + int flags = 0; + +- if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0) ++ if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0 || ++ mnt_fs_get_option(fstab_fs, "rbind", NULL, NULL) == 0) + flags = MS_BIND; + + rootfs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root); +-- +2.14.4 + diff --git a/SOURCES/0161-mount-add-ext4-to-some-places-in-man-page.patch b/SOURCES/0161-mount-add-ext4-to-some-places-in-man-page.patch new file mode 100644 index 0000000..00d2d5d --- /dev/null +++ b/SOURCES/0161-mount-add-ext4-to-some-places-in-man-page.patch @@ -0,0 +1,54 @@ +From 7492cf62b7334792df931469ca783878b868d0d0 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 6 Jun 2018 12:10:42 +0200 +Subject: [PATCH 161/173] mount: add ext4 to some places in man page + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1538721 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/mount.8 | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/sys-utils/mount.8 b/sys-utils/mount.8 +index 49cb2818f..35d782f0e 100644 +--- a/sys-utils/mount.8 ++++ b/sys-utils/mount.8 +@@ -874,7 +874,7 @@ for example tune2fs -l output for extN filesystems). + The following options apply to any filesystem that is being + mounted (but not every filesystem actually honors them - e.g., the + .B sync +-option today has effect only for ext2, ext3, fat, vfat and ufs): ++option today has effect only for ext2, ext3, ext4, fat, vfat and ufs): + + .TP + .B async +@@ -909,7 +909,7 @@ The + .BR context= + option is useful when mounting filesystems that do not support + extended attributes, such as a floppy or hard disk formatted with VFAT, or +-systems that are not normally running under SELinux, such as an ext3 formatted ++systems that are not normally running under SELinux, such as an ext3 or ext4 formatted + disk from a non-SELinux workstation. You can also use + .BR context= + on filesystems you do not trust, such as a floppy. It also helps in compatibility with +@@ -2833,7 +2833,7 @@ not specified or the filesystem is known for libblkid, for example: + .sp + .B "mount /tmp/disk.img /mnt" + .sp +-.B "mount -t ext3 /tmp/disk.img /mnt" ++.B "mount -t ext4 /tmp/disk.img /mnt" + .sp + .RE + This type of mount knows about four options, namely +@@ -2948,7 +2948,7 @@ It is possible for a corrupted filesystem to cause a crash. + .PP + Some Linux filesystems don't support + .B "\-o sync and \-o dirsync" +-(the ext2, ext3, fat and vfat filesystems ++(the ext2, ext3, ext4, fat and vfat filesystems + .I do + support synchronous updates (a la BSD) when mounted with the + .B sync +-- +2.14.4 + diff --git a/SOURCES/0162-agetty-keep-c_iflags-unmodified-on-autologin.patch b/SOURCES/0162-agetty-keep-c_iflags-unmodified-on-autologin.patch new file mode 100644 index 0000000..a14f4c4 --- /dev/null +++ b/SOURCES/0162-agetty-keep-c_iflags-unmodified-on-autologin.patch @@ -0,0 +1,72 @@ +From 7974045302fe53629e70501f0180f30cbaa25b1e Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 6 Jun 2018 15:57:24 +0200 +Subject: [PATCH 162/173] agetty: keep c_iflags unmodified on --autologin + +agetty sets c_iflags according to interaction with serial line in +get_logname(). For --autologin it does not read from the line, so we +have no clue how to set the flags. + +The current behavior is to zeroize the flags. Unfortunately, it seems +like bad idea, because the line may be already properly initialized by +kernel (or systemd, etc.). + +The new behavior is not touch the flags on --autologin. + +Upstream: http://github.com/karelzak/util-linux/commit/2c4d86abdadab19be76abecb156da7f7dc284d81 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1252764 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + term-utils/agetty.8 | 4 ++++ + term-utils/agetty.c | 17 +++++++++++++---- + 2 files changed, 17 insertions(+), 4 deletions(-) + +diff --git a/term-utils/agetty.8 b/term-utils/agetty.8 +index a42cdf158..fe4bfd427 100644 +--- a/term-utils/agetty.8 ++++ b/term-utils/agetty.8 +@@ -94,6 +94,10 @@ password. The \-f \fIusername\fP option is added to the \fB/bin/login\fP + command line by default. The \-\-login-options option changes this default + behaviour and then only \\u is replaced by the \fIusername\fP and no other + option is added to the login command line. ++ ++Note that \fB\-\-autologin\fP may affect the way how agetty initializes the ++serial line, because on auto-login agetty does not read from the line and it ++has no opportunity optimize the line setting. + .TP + \-c, \-\-noreset + Don't reset terminal cflags (control modes). See \fItermios(3)\fP for more +diff --git a/term-utils/agetty.c b/term-utils/agetty.c +index b626cdbeb..948d77246 100644 +--- a/term-utils/agetty.c ++++ b/term-utils/agetty.c +@@ -1100,13 +1100,22 @@ static void termio_init(struct options *op, struct termios *tp) + /* Flush input and output queues, important for modems! */ + tcflush(STDIN_FILENO, TCIOFLUSH); + ++ /* The defaul is set c_iflag in termio_final() according to chardata. ++ * Unfortunately, the chardata are not set according to the serial line ++ * if --autolog is enabled. In this case we do not read from the line ++ * at all. The best what we can do in this case is to keep c_iflag ++ * unmodified for --autolog. ++ */ ++ if (!op->autolog) { + #ifdef IUTF8 +- tp->c_iflag = tp->c_iflag & IUTF8; +- if (tp->c_iflag & IUTF8) +- op->flags |= F_UTF8; ++ tp->c_iflag = tp->c_iflag & IUTF8; ++ if (tp->c_iflag & IUTF8) ++ op->flags |= F_UTF8; + #else +- tp->c_iflag = 0; ++ tp->c_iflag = 0; + #endif ++ } ++ + tp->c_lflag = 0; + tp->c_oflag &= OPOST | ONLCR; + +-- +2.14.4 + diff --git a/SOURCES/0163-sulogin-don-t-use-strcpy-enlarge-pwd-line-buffer.patch b/SOURCES/0163-sulogin-don-t-use-strcpy-enlarge-pwd-line-buffer.patch new file mode 100644 index 0000000..d3b7610 --- /dev/null +++ b/SOURCES/0163-sulogin-don-t-use-strcpy-enlarge-pwd-line-buffer.patch @@ -0,0 +1,73 @@ +From 94f380e223e7496804dcd68e204fba0a15df8bd7 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 25 May 2015 15:24:13 +0200 +Subject: [PATCH 163/173] sulogin: don't use strcpy(), enlarge pwd line buffer + +* according to "man getpwnam" 16384 bytes is enough to store one + passwd entry (let's use 2*BUFSIZE to avoid magic numbers in code) + +* don't use strcpy() to set empty password + +Upstream: http://github.com/karelzak/util-linux/commit/d681e0956cdca1a016346424939fe1b9c6a0a549 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1561200 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + login-utils/sulogin.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c +index bbd67b3ee..6d03bc5ae 100644 +--- a/login-utils/sulogin.c ++++ b/login-utils/sulogin.c +@@ -373,8 +373,8 @@ static struct passwd *getrootpwent(int try_manually) + struct passwd *pw; + struct spwd *spw; + FILE *fp; +- static char line[256]; +- static char sline[256]; ++ static char line[2 * BUFSIZ]; ++ static char sline[2 * BUFSIZ]; + char *p; + + /* +@@ -410,7 +410,7 @@ static struct passwd *getrootpwent(int try_manually) + /* + * Find root in the password file. + */ +- while ((p = fgets(line, 256, fp)) != NULL) { ++ while ((p = fgets(line, sizeof(line), fp)) != NULL) { + if (strncmp(line, "root:", 5) != 0) + continue; + p += 5; +@@ -439,12 +439,12 @@ static struct passwd *getrootpwent(int try_manually) + /* + * The password is invalid. If there is a shadow password, try it. + */ +- strcpy(pwd.pw_passwd, ""); ++ *pwd.pw_passwd = '\0'; + if ((fp = fopen(_PATH_SHADOW_PASSWD, "r")) == NULL) { + warn(_("cannot open %s"), _PATH_PASSWD); + return &pwd; + } +- while ((p = fgets(sline, 256, fp)) != NULL) { ++ while ((p = fgets(sline, sizeof(sline), fp)) != NULL) { + if (strncmp(sline, "root:", 5) != 0) + continue; + p += 5; +@@ -458,11 +458,11 @@ static struct passwd *getrootpwent(int try_manually) + */ + if (p == NULL) { + warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD); +- strcpy(pwd.pw_passwd, ""); ++ *pwd.pw_passwd = '\0'; + } + if (!valid(pwd.pw_passwd)) { + warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD); +- strcpy(pwd.pw_passwd, ""); ++ *pwd.pw_passwd = '\0'; + } + return &pwd; + } +-- +2.14.4 + diff --git a/SOURCES/0164-sulogin-improve-support-for-locked-root-account.patch b/SOURCES/0164-sulogin-improve-support-for-locked-root-account.patch new file mode 100644 index 0000000..9aa2443 --- /dev/null +++ b/SOURCES/0164-sulogin-improve-support-for-locked-root-account.patch @@ -0,0 +1,175 @@ +From a08c1a6aadaa27e4070fe58242d55464d6c7a3e7 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 25 May 2015 15:30:52 +0200 +Subject: [PATCH 164/173] sulogin: improve support for locked root account + +Some installations and distributions don't use a root account password +for security reasons and use sudo instead. In that case, asking for the +password makes no sense, and it is not even considered as valid as it's just +"*" or "!". + +In these cases --force is required to just start a root shell and no +ask for password. + +I don't think it's a good idea to automatically start root shell when +locked account is detected. It's possible that the machine is on +public place and for example Ubuntu uses root account disabled by +default (and also Fedora when installed by yum/dnf without anaconda). + +The --force option forces admins to think about it... + +The distro maintainers can also use --force in their initscripts or +systemd emergency.service if they believe that promiscuous setting is +the right thing for the distro. + +Addresses: https://bugs.debian.org/326678 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1561200 +Upstream: http://github.com/karelzak/util-linux/commit/7ff1162e67164cb4ece19dd809c26272461aa254 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + login-utils/sulogin.8 | 13 +++++++++---- + login-utils/sulogin.c | 48 ++++++++++++++++++++++++++++++++++-------------- + 2 files changed, 43 insertions(+), 18 deletions(-) + +diff --git a/login-utils/sulogin.8 b/login-utils/sulogin.8 +index b9dec165c..702487985 100644 +--- a/login-utils/sulogin.8 ++++ b/login-utils/sulogin.8 +@@ -35,8 +35,10 @@ Give root password for system maintenance + .br + (or type Control\-D for normal startup): + .PP +-.I sulogin +-will be connected to the current terminal, or to the optional tty device that ++If the root account is locked and --force is specified, no password is required. ++.PP ++.B sulogin ++will be connected to the current terminal, or to the optional \fItty\fR device that + can be specified on the command line (typically + .BR /dev/console ). + .PP +@@ -50,8 +52,11 @@ from the system fails, manually examine + .I /etc/passwd + and + .I /etc/shadow +-to get the password. If they are damaged or nonexistent, sulogin will start +-a root shell without asking for a password. ++to get the password. If these files are damaged or nonexistent, or when ++root account is locked by '!' or '*' at the begin of the password then ++.B sulogin ++will \fBstart a root shell without asking for a password\fP. ++.PP + .IP + Only use the + .B \-e +diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c +index 6d03bc5ae..a17b91d71 100644 +--- a/login-utils/sulogin.c ++++ b/login-utils/sulogin.c +@@ -81,6 +81,13 @@ static volatile sig_atomic_t sigchild; + # define IUCLC 0 + #endif + ++static int locked_account_password(const char *passwd) ++{ ++ if (passwd && (*passwd == '*' || *passwd == '!')) ++ return 1; ++ return 0; ++} ++ + /* + * Fix the tty modes and set reasonable defaults. + */ +@@ -423,7 +430,6 @@ static struct passwd *getrootpwent(int try_manually) + p = line; + break; + } +- + fclose(fp); + + /* +@@ -460,7 +466,8 @@ static struct passwd *getrootpwent(int try_manually) + warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD); + *pwd.pw_passwd = '\0'; + } +- if (!valid(pwd.pw_passwd)) { ++ /* locked accont passwords are valid too */ ++ if (!locked_account_password(pwd.pw_passwd) && !valid(pwd.pw_passwd)) { + warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD); + *pwd.pw_passwd = '\0'; + } +@@ -470,7 +477,7 @@ static struct passwd *getrootpwent(int try_manually) + /* + * Ask by prompt for the password. + */ +-static void doprompt(const char *crypted, struct console *con) ++static void doprompt(const char *crypted, struct console *con, int deny) + { + struct termios tty; + +@@ -487,18 +494,25 @@ static void doprompt(const char *crypted, struct console *con) + if ((con->file = fdopen(con->fd, "r+")) == (FILE*)0) + goto err; + } ++ ++ if (deny) ++ fprintf(con->file, _("\nCannot open access to console, the root account is locked.\n" ++ "See sulogin(8) man page for more details.\n\n" ++ "Press Enter to continue.\n")); ++ else { + #if defined(USE_ONELINE) +- if (crypted[0]) +- fprintf(con->file, _("Give root password for login: ")); +- else +- fprintf(con->file, _("Press enter for login: ")); ++ if (crypted[0] && !locked_account_password(crypted)) ++ fprintf(con->file, _("Give root password for login: ")); ++ else ++ fprintf(con->file, _("Press Enter for login: ")); + #else +- if (crypted[0]) +- fprintf(con->file, _("Give root password for maintenance\n")); +- else +- fprintf(con->file, _("Press enter for maintenance")); +- fprintf(con->file, _("(or type Control-D to continue): ")); ++ if (crypted[0] && !locked_account_password(crypted)) ++ fprintf(con->file, _("Give root password for maintenance\n")); ++ else ++ fprintf(con->file, _("Press Enter for maintenance\n")); ++ fprintf(con->file, _("(or press Control-D to continue): ")); + #endif ++ } + fflush(con->file); + err: + if (con->flags & CON_SERIAL) +@@ -914,6 +928,7 @@ int main(int argc, char **argv) + goto nofork; + } + ++ + mask_signal(SIGCHLD, chld_handler, &saved_sigchld); + do { + con = list_entry(ptr, struct console, entry); +@@ -930,12 +945,17 @@ int main(int argc, char **argv) + const char *passwd = pwd->pw_passwd; + const char *answer; + int failed = 0, doshell = 0; ++ int deny = !opt_e && locked_account_password(pwd->pw_passwd); ++ ++ doprompt(passwd, con, deny); + +- doprompt(passwd, con); + if ((answer = getpasswd(con)) == NULL) + break; ++ if (deny) ++ exit(EXIT_FAILURE); + +- if (passwd[0] == '\0') ++ /* no password or locked account */ ++ if (!passwd[0] || locked_account_password(passwd)) + doshell++; + else { + const char *cryptbuf; +-- +2.14.4 + diff --git a/SOURCES/0165-sulogin-Always-make-echo-work-after-performing-getpa.patch b/SOURCES/0165-sulogin-Always-make-echo-work-after-performing-getpa.patch new file mode 100644 index 0000000..abc0cb9 --- /dev/null +++ b/SOURCES/0165-sulogin-Always-make-echo-work-after-performing-getpa.patch @@ -0,0 +1,34 @@ +From 6bb8e6d4c38f260109852ef8b43876ea1aff3810 Mon Sep 17 00:00:00 2001 +From: Werner Fink <werner@suse.de> +Date: Tue, 22 Mar 2016 10:38:59 +0100 +Subject: [PATCH 165/173] sulogin: Always make echo work after performing + getpasswd even if root account is locked + +If the root account is locked and no password was provided then the terminal +line is not set back to do echo of the input. This correct a small overlook +in commit 7ff1162e67164cb4ece19dd809c26272461aa254 + +Upstream: http://github.com/karelzak/util-linux/commit/6988998b66b4b95c494d60599f8e3de2ffbaeece +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1561200 +Signed-off-by: Werner Fink <werner@suse.de> +--- + login-utils/sulogin.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c +index a17b91d71..a6918282e 100644 +--- a/login-utils/sulogin.c ++++ b/login-utils/sulogin.c +@@ -676,8 +676,7 @@ quit: + alarm(0); + if (tc) + tcsetattr(fd, TCSAFLUSH, &con->tio); +- if (ret && *ret != '\0') +- tcfinal(con); ++ tcfinal(con); + printf("\r\n"); + out: + return ret; +-- +2.14.4 + diff --git a/SOURCES/0166-sulogin-make-getpasswd-.-return-NULL-on-D.patch b/SOURCES/0166-sulogin-make-getpasswd-.-return-NULL-on-D.patch new file mode 100644 index 0000000..fb8e29d --- /dev/null +++ b/SOURCES/0166-sulogin-make-getpasswd-.-return-NULL-on-D.patch @@ -0,0 +1,35 @@ +From c19feb058ff5883ab7255241d13732ab66bf44f0 Mon Sep 17 00:00:00 2001 +From: Andreas Henriksson <andreas@fatal.se> +Date: Mon, 28 Nov 2016 17:24:49 +0100 +Subject: [PATCH 166/173] sulogin: make getpasswd(...) return NULL on ^D +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This makes the caller bail out early instead of evaluating the +input as a password. + +Reported-by: Bjørn Mork <bjorn@mork.no> +Addresses: http://bugs.debian.org/846112 +Upstream: http://github.com/karelzak/util-linux/commit/60dea9d187caa700e42f37c7955116f71be912d5 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1561200 +Signed-off-by: Andreas Henriksson <andreas@fatal.se> +--- + login-utils/sulogin.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c +index a6918282e..7ec349953 100644 +--- a/login-utils/sulogin.c ++++ b/login-utils/sulogin.c +@@ -661,6 +661,7 @@ static char *getpasswd(struct console *con) + ptr--; + break; + case CEOF: ++ ret = NULL; + goto quit; + default: + if ((size_t)(ptr - &pass[0]) >= (sizeof(pass) -1 )) { +-- +2.14.4 + diff --git a/SOURCES/0167-sulogin-bail-out-from-getpasswd-.-on-timeout.patch b/SOURCES/0167-sulogin-bail-out-from-getpasswd-.-on-timeout.patch new file mode 100644 index 0000000..e0f72a7 --- /dev/null +++ b/SOURCES/0167-sulogin-bail-out-from-getpasswd-.-on-timeout.patch @@ -0,0 +1,38 @@ +From 971b2ecede4d2040e879d9cfb56332a2e210bed8 Mon Sep 17 00:00:00 2001 +From: Andreas Henriksson <andreas@fatal.se> +Date: Mon, 28 Nov 2016 17:24:50 +0100 +Subject: [PATCH 167/173] sulogin: bail out from getpasswd(...) on timeout +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If timeout happens while waiting in prompt, bail out instead +of retrying. + +Reported-by: Bjørn Mork <bjorn@mork.no> +Addresses: http://bugs.debian.org/846107 +Upstream: http://github.com/karelzak/util-linux/commit/1c4b2d43926e2a7032310cd18b411d8d872cb4ed +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1561200 +Signed-off-by: Andreas Henriksson <andreas@fatal.se> +--- + login-utils/sulogin.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c +index 7ec349953..dd73a1c50 100644 +--- a/login-utils/sulogin.c ++++ b/login-utils/sulogin.c +@@ -611,6 +611,10 @@ static char *getpasswd(struct console *con) + while (cp->eol == '\0') { + if (read(fd, &c, 1) < 1) { + if (errno == EINTR || errno == EAGAIN) { ++ if (alarm_rised) { ++ ret = NULL; ++ goto quit; ++ } + usleep(1000); + continue; + } +-- +2.14.4 + diff --git a/SOURCES/0168-losetup-keep-f-and-devname-mutually-exclusive.patch b/SOURCES/0168-losetup-keep-f-and-devname-mutually-exclusive.patch new file mode 100644 index 0000000..28278f3 --- /dev/null +++ b/SOURCES/0168-losetup-keep-f-and-devname-mutually-exclusive.patch @@ -0,0 +1,40 @@ +From 14a4f41a94cc4bd227940c4bd976e39d9f13e8c5 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 7 Jun 2018 12:05:08 +0200 +Subject: [PATCH 168/173] losetup: keep -f and <devname> mutually exclusive + +losetup tries to blindly use specified device as well as search for +the first free device, the result is: + + # losetup /dev/loop1 -f /tmp/tfile_loop1 + losetup: /dev/loop1: failed to set up loop device: Invalid argument + +fixed version: + + # losetup /dev/loop10 -f img + losetup: unexpected arguments + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1566432 +Upstream: http://github.com/karelzak/util-linux/commit/c3f5a0f1d47dbc47f6d21da232d4eb1cfb7905db +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/losetup.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/sys-utils/losetup.c b/sys-utils/losetup.c +index d9b0c9b61..8df831b8f 100644 +--- a/sys-utils/losetup.c ++++ b/sys-utils/losetup.c +@@ -545,6 +545,9 @@ int main(int argc, char **argv) + */ + act = A_CREATE; + file = argv[optind++]; ++ ++ if (optind < argc) ++ errx(EXIT_FAILURE, _("unexpected arguments")); + } + + if (list && !act && optind == argc) +-- +2.14.4 + diff --git a/SOURCES/0169-lscpu-fix-mzx-min-MHz-reporting.patch b/SOURCES/0169-lscpu-fix-mzx-min-MHz-reporting.patch new file mode 100644 index 0000000..dd53555 --- /dev/null +++ b/SOURCES/0169-lscpu-fix-mzx-min-MHz-reporting.patch @@ -0,0 +1,83 @@ +From 79866c4b187e9ccf37fe333e9865a8575f4a3a16 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 6 Jun 2018 10:10:20 +0200 +Subject: [PATCH 169/173] lscpu: fix mzx/min MHz reporting + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1579439 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + sys-utils/lscpu.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 48 insertions(+), 2 deletions(-) + +diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c +index 1ee73f34a..4c15de1d4 100644 +--- a/sys-utils/lscpu.c ++++ b/sys-utils/lscpu.c +@@ -1251,6 +1251,52 @@ read_configured(struct lscpu_desc *desc, int idx) + desc->configured[idx] = path_read_s32(_PATH_SYS_CPU "/cpu%d/configure", num); + } + ++/* Read overall maximum frequency of cpu */ ++static char * ++cpu_max_mhz(struct lscpu_desc *desc, char *buf, size_t bufsz) ++{ ++ int i; ++ float cpu_freq = 0.0; ++ size_t setsize = CPU_ALLOC_SIZE(maxcpus); ++ ++ if (desc->present) { ++ for (i = 0; i < desc->ncpuspos; i++) { ++ if (CPU_ISSET_S(real_cpu_num(desc, i), setsize, desc->present) ++ && desc->maxmhz[i]) { ++ float freq = atof(desc->maxmhz[i]); ++ ++ if (freq > cpu_freq) ++ cpu_freq = freq; ++ } ++ } ++ } ++ snprintf(buf, bufsz, "%.4f", cpu_freq); ++ return buf; ++} ++ ++/* Read overall minimum frequency of cpu */ ++static char * ++cpu_min_mhz(struct lscpu_desc *desc, char *buf, size_t bufsz) ++{ ++ int i; ++ float cpu_freq = -1.0; ++ size_t setsize = CPU_ALLOC_SIZE(maxcpus); ++ ++ if (desc->present) { ++ for (i = 0; i < desc->ncpuspos; i++) { ++ if (CPU_ISSET_S(real_cpu_num(desc, i), setsize, desc->present) ++ && desc->minmhz[i]) { ++ float freq = atof(desc->minmhz[i]); ++ ++ if (cpu_freq < 0.0 || freq < cpu_freq) ++ cpu_freq = freq; ++ } ++ } ++ } ++ snprintf(buf, bufsz, "%.4f", cpu_freq); ++ return buf; ++} ++ + static void + read_max_mhz(struct lscpu_desc *desc, int idx) + { +@@ -1898,9 +1944,9 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod) + if (desc->static_mhz) + print_s(_("CPU static MHz:"), desc->static_mhz); + if (desc->maxmhz && desc->maxmhz[0]) +- print_s(_("CPU max MHz:"), desc->maxmhz[0]); ++ print_s(_("CPU max MHz:"), cpu_max_mhz(desc, buf, sizeof(buf))); + if (desc->minmhz && desc->minmhz[0]) +- print_s(_("CPU min MHz:"), desc->minmhz[0]); ++ print_s(_("CPU min MHz:"), cpu_min_mhz(desc, buf, sizeof(buf))); + if (desc->bogomips) + print_s(_("BogoMIPS:"), desc->bogomips); + if (desc->virtflag) { +-- +2.14.4 + diff --git a/SOURCES/0170-chcpu-cleanup-return-codes.patch b/SOURCES/0170-chcpu-cleanup-return-codes.patch new file mode 100644 index 0000000..147d201 --- /dev/null +++ b/SOURCES/0170-chcpu-cleanup-return-codes.patch @@ -0,0 +1,253 @@ +From 8f57ecb12194a10f3dbe6eca9f4bf0b18c4be757 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 5 Mar 2014 11:06:59 +0100 +Subject: [PATCH 170/173] chcpu: cleanup return codes + +The code currently always return EXIT_SUCCESS, that's strange. It +seems better to return 0 on success, 1 on complete failure and 64 on +partial success. + +Signed-off-by: Karel Zak <kzak@redhat.com> +Upstream: http://github.com/karelzak/util-linux/commit/48fc00c1c70f3dbbd8ad6ef423bbba27dd3efb69 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1579439 +--- + sys-utils/chcpu.8 | 14 ++++++++++ + sys-utils/chcpu.c | 82 ++++++++++++++++++++++++++++++++++++++++--------------- + 2 files changed, 74 insertions(+), 22 deletions(-) + +diff --git a/sys-utils/chcpu.8 b/sys-utils/chcpu.8 +index d016b86f2..125d9d2ad 100644 +--- a/sys-utils/chcpu.8 ++++ b/sys-utils/chcpu.8 +@@ -80,6 +80,20 @@ Display help information and exit. + .TP + .BR \-V , " \-\-version" + Display version information and exit. ++ ++.SH RETURN CODES ++.B chcpu ++has the following return codes: ++.TP ++.BR 0 ++success ++.TP ++.BR 1 ++failure ++.TP ++.BR 64 ++partial success ++.RE + .SH AUTHOR + .MT heiko.carstens@de.ibm.com + Heiko Carstens +diff --git a/sys-utils/chcpu.c b/sys-utils/chcpu.c +index 1162888d5..304b80d7a 100644 +--- a/sys-utils/chcpu.c ++++ b/sys-utils/chcpu.c +@@ -45,6 +45,9 @@ + + #define EXCL_ERROR "--{configure,deconfigure,disable,dispatch,enable}" + ++/* partial success, otherwise we return regular EXIT_{SUCCESS,FAILURE} */ ++#define CHCPU_EXIT_SOMEOK 64 ++ + #define _PATH_SYS_CPU "/sys/devices/system/cpu" + #define _PATH_SYS_CPU_ONLINE _PATH_SYS_CPU "/online" + #define _PATH_SYS_CPU_RESCAN _PATH_SYS_CPU "/rescan" +@@ -66,21 +69,28 @@ enum { + CMD_CPU_DISPATCH_VERTICAL, + }; + ++/* returns: 0 = success ++ * < 0 = failure ++ * > 0 = partial success ++ */ + static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable) + { + unsigned int cpu; + int online, rc; + int configured = -1; ++ size_t fails = 0; + + for (cpu = 0; cpu < setsize; cpu++) { + if (!CPU_ISSET(cpu, cpu_set)) + continue; + if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) { + printf(_("CPU %d does not exist\n"), cpu); ++ fails++; + continue; + } + if (!path_exist(_PATH_SYS_CPU "/cpu%d/online", cpu)) { + printf(_("CPU %d is not hot pluggable\n"), cpu); ++ fails++; + continue; + } + online = path_read_s32(_PATH_SYS_CPU "/cpu%d/online", cpu); +@@ -96,30 +106,35 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable) + configured = path_read_s32(_PATH_SYS_CPU "/cpu%d/configure", cpu); + if (enable) { + rc = path_write_str("1", _PATH_SYS_CPU "/cpu%d/online", cpu); +- if ((rc == -1) && (configured == 0)) ++ if ((rc == -1) && (configured == 0)) { + warnx(_("CPU %d enable failed " + "(CPU is deconfigured)"), cpu); +- else if (rc == -1) ++ fails++; ++ } else if (rc == -1) { + warn(_("CPU %d enable failed"), cpu); +- else ++ fails++; ++ } else + printf(_("CPU %d enabled\n"), cpu); + } else { + if (onlinecpus && num_online_cpus() == 1) { + printf(_("CPU %d disable failed " + "(last enabled CPU)\n"), cpu); ++ fails++; + continue; + } + rc = path_write_str("0", _PATH_SYS_CPU "/cpu%d/online", cpu); +- if (rc == -1) ++ if (rc == -1) { + warn(_("CPU %d disable failed"), cpu); +- else { ++ fails++; ++ } else { + printf(_("CPU %d disabled\n"), cpu); + if (onlinecpus) + CPU_CLR(cpu, onlinecpus); + } + } + } +- return EXIT_SUCCESS; ++ ++ return fails == 0 ? 0 : fails == setsize ? -1 : 1; + } + + static int cpu_rescan(void) +@@ -129,7 +144,7 @@ static int cpu_rescan(void) + if (path_write_str("1", _PATH_SYS_CPU_RESCAN) == -1) + err(EXIT_FAILURE, _("Failed to trigger rescan of CPUs")); + printf(_("Triggered rescan of CPUs\n")); +- return EXIT_SUCCESS; ++ return 0; + } + + static int cpu_set_dispatch(int mode) +@@ -146,23 +161,30 @@ static int cpu_set_dispatch(int mode) + err(EXIT_FAILURE, _("Failed to set vertical dispatch mode")); + printf(_("Successfully set vertical dispatching mode\n")); + } +- return EXIT_SUCCESS; ++ return 0; + } + ++/* returns: 0 = success ++ * < 0 = failure ++ * > 0 = partial success ++ */ + static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure) + { + unsigned int cpu; + int rc, current; ++ size_t fails = 0; + + for (cpu = 0; cpu < setsize; cpu++) { + if (!CPU_ISSET(cpu, cpu_set)) + continue; + if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) { + printf(_("CPU %d does not exist\n"), cpu); ++ fails++; + continue; + } + if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", cpu)) { + printf(_("CPU %d is not configurable\n"), cpu); ++ fails++; + continue; + } + current = path_read_s32(_PATH_SYS_CPU "/cpu%d/configure", cpu); +@@ -178,23 +200,27 @@ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure) + is_cpu_online(cpu)) { + printf(_("CPU %d deconfigure failed " + "(CPU is enabled)\n"), cpu); ++ fails++; + continue; + } + if (configure) { + rc = path_write_str("1", _PATH_SYS_CPU "/cpu%d/configure", cpu); +- if (rc == -1) ++ if (rc == -1) { + warn(_("CPU %d configure failed"), cpu); +- else ++ fails++; ++ } else + printf(_("CPU %d configured\n"), cpu); + } else { + rc = path_write_str("0", _PATH_SYS_CPU "/cpu%d/configure", cpu); +- if (rc == -1) ++ if (rc == -1) { + warn(_("CPU %d deconfigure failed"), cpu); +- else ++ fails++; ++ } else + printf(_("CPU %d deconfigured\n"), cpu); + } + } +- return EXIT_SUCCESS; ++ ++ return fails == 0 ? 0 : fails == setsize ? -1 : 1; + } + + static void cpu_parse(char *cpu_string, cpu_set_t *cpu_set, size_t setsize) +@@ -233,7 +259,7 @@ int main(int argc, char *argv[]) + cpu_set_t *cpu_set; + size_t setsize; + int cmd = -1; +- int c; ++ int c, rc; + + static const struct option longopts[] = { + { "configure", required_argument, 0, 'c' }, +@@ -317,19 +343,31 @@ int main(int argc, char *argv[]) + + switch (cmd) { + case CMD_CPU_ENABLE: +- return cpu_enable(cpu_set, maxcpus, 1); ++ rc = cpu_enable(cpu_set, maxcpus, 1); ++ break; + case CMD_CPU_DISABLE: +- return cpu_enable(cpu_set, maxcpus, 0); ++ rc = cpu_enable(cpu_set, maxcpus, 0); ++ break; + case CMD_CPU_CONFIGURE: +- return cpu_configure(cpu_set, maxcpus, 1); ++ rc = cpu_configure(cpu_set, maxcpus, 1); ++ break; + case CMD_CPU_DECONFIGURE: +- return cpu_configure(cpu_set, maxcpus, 0); ++ rc = cpu_configure(cpu_set, maxcpus, 0); ++ break; + case CMD_CPU_RESCAN: +- return cpu_rescan(); ++ rc = cpu_rescan(); ++ break; + case CMD_CPU_DISPATCH_HORIZONTAL: +- return cpu_set_dispatch(0); ++ rc = cpu_set_dispatch(0); ++ break; + case CMD_CPU_DISPATCH_VERTICAL: +- return cpu_set_dispatch(1); ++ rc = cpu_set_dispatch(1); ++ break; ++ default: ++ rc = -EINVAL; ++ break; + } +- return EXIT_SUCCESS; ++ ++ return rc == 0 ? EXIT_SUCCESS : ++ rc < 0 ? EXIT_FAILURE : CHCPU_EXIT_SOMEOK; + } +-- +2.14.4 + diff --git a/SOURCES/0171-chcpu-cleanup-stdout-stderr-usage.patch b/SOURCES/0171-chcpu-cleanup-stdout-stderr-usage.patch new file mode 100644 index 0000000..8f500a8 --- /dev/null +++ b/SOURCES/0171-chcpu-cleanup-stdout-stderr-usage.patch @@ -0,0 +1,79 @@ +From 26c75797103da70f85a5fdd646625edb3eaa300c Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 5 Mar 2014 11:23:16 +0100 +Subject: [PATCH 171/173] chcpu: cleanup stdout/stderr usage + +Signed-off-by: Karel Zak <kzak@redhat.com> +Upstream: http://github.com/karelzak/util-linux/commit/69e74525bb3212944feebc8c2848317e04d95ec0 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1579439 +--- + sys-utils/chcpu.c | 17 +++++++---------- + 1 file changed, 7 insertions(+), 10 deletions(-) + +diff --git a/sys-utils/chcpu.c b/sys-utils/chcpu.c +index 304b80d7a..ada0eaacc 100644 +--- a/sys-utils/chcpu.c ++++ b/sys-utils/chcpu.c +@@ -84,12 +84,12 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable) + if (!CPU_ISSET(cpu, cpu_set)) + continue; + if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) { +- printf(_("CPU %d does not exist\n"), cpu); ++ warnx(_("CPU %d does not exist"), cpu); + fails++; + continue; + } + if (!path_exist(_PATH_SYS_CPU "/cpu%d/online", cpu)) { +- printf(_("CPU %d is not hot pluggable\n"), cpu); ++ warnx(_("CPU %d is not hot pluggable"), cpu); + fails++; + continue; + } +@@ -107,8 +107,7 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable) + if (enable) { + rc = path_write_str("1", _PATH_SYS_CPU "/cpu%d/online", cpu); + if ((rc == -1) && (configured == 0)) { +- warnx(_("CPU %d enable failed " +- "(CPU is deconfigured)"), cpu); ++ warn(_("CPU %d enable failed (CPU is deconfigured)"), cpu); + fails++; + } else if (rc == -1) { + warn(_("CPU %d enable failed"), cpu); +@@ -117,8 +116,7 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable) + printf(_("CPU %d enabled\n"), cpu); + } else { + if (onlinecpus && num_online_cpus() == 1) { +- printf(_("CPU %d disable failed " +- "(last enabled CPU)\n"), cpu); ++ warnx(_("CPU %d disable failed (last enabled CPU)"), cpu); + fails++; + continue; + } +@@ -178,12 +176,12 @@ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure) + if (!CPU_ISSET(cpu, cpu_set)) + continue; + if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) { +- printf(_("CPU %d does not exist\n"), cpu); ++ warnx(_("CPU %d does not exist"), cpu); + fails++; + continue; + } + if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", cpu)) { +- printf(_("CPU %d is not configurable\n"), cpu); ++ warnx(_("CPU %d is not configurable"), cpu); + fails++; + continue; + } +@@ -198,8 +196,7 @@ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure) + } + if ((current == 1) && (configure == 0) && onlinecpus && + is_cpu_online(cpu)) { +- printf(_("CPU %d deconfigure failed " +- "(CPU is enabled)\n"), cpu); ++ warnx(_("CPU %d deconfigure failed (CPU is enabled)"), cpu); + fails++; + continue; + } +-- +2.14.4 + diff --git a/SOURCES/0172-lscpu-chcpu-Avoid-use-of-the-old-CPU-macros.patch b/SOURCES/0172-lscpu-chcpu-Avoid-use-of-the-old-CPU-macros.patch new file mode 100644 index 0000000..30822d1 --- /dev/null +++ b/SOURCES/0172-lscpu-chcpu-Avoid-use-of-the-old-CPU-macros.patch @@ -0,0 +1,86 @@ +From 255a3b8bb8cbcb3f689cc3332983983bfbf9eca0 Mon Sep 17 00:00:00 2001 +From: Stanislav Brabec <sbrabec@suse.cz> +Date: Wed, 9 May 2018 18:08:32 +0200 +Subject: [PATCH 172/173] lscpu, chcpu: Avoid use of the old CPU macros + +The old CPU macros are limited to 1024 cores. As a result, lscpu cannot +count sockets on large systems. Use new scalable macros. + +Signed-off-by: Stanislav Brabec <sbrabec@suse.cz> +Cc: Michael Matz <matz@suse.de> +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1579439 +Upstream: http://github.com/karelzak/util-linux/commit/538b50cb0a4aac56b6b3b6e4d1e8ce886854c6d8 +--- + sys-utils/chcpu.c | 6 +++--- + sys-utils/lscpu.c | 7 +++++-- + 2 files changed, 8 insertions(+), 5 deletions(-) + +diff --git a/sys-utils/chcpu.c b/sys-utils/chcpu.c +index ada0eaacc..7843dfb22 100644 +--- a/sys-utils/chcpu.c ++++ b/sys-utils/chcpu.c +@@ -81,7 +81,7 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable) + size_t fails = 0; + + for (cpu = 0; cpu < setsize; cpu++) { +- if (!CPU_ISSET(cpu, cpu_set)) ++ if (!CPU_ISSET_S(cpu, setsize, cpu_set)) + continue; + if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) { + warnx(_("CPU %d does not exist"), cpu); +@@ -127,7 +127,7 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable) + } else { + printf(_("CPU %d disabled\n"), cpu); + if (onlinecpus) +- CPU_CLR(cpu, onlinecpus); ++ CPU_CLR_S(cpu, setsize, onlinecpus); + } + } + } +@@ -173,7 +173,7 @@ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure) + size_t fails = 0; + + for (cpu = 0; cpu < setsize; cpu++) { +- if (!CPU_ISSET(cpu, cpu_set)) ++ if (!CPU_ISSET_S(cpu, setsize, cpu_set)) + continue; + if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) { + warnx(_("CPU %d does not exist"), cpu); +diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c +index 4c15de1d4..002ad0d1c 100644 +--- a/sys-utils/lscpu.c ++++ b/sys-utils/lscpu.c +@@ -626,7 +626,7 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod) + desc->idx2cpunum = xcalloc(desc->ncpuspos, sizeof(int)); + + for (num = 0, idx = 0; num < maxcpus; num++) { +- if (CPU_ISSET(num, tmp)) ++ if (CPU_ISSET_S(num, setsize, tmp)) + desc->idx2cpunum[idx++] = num; + } + cpuset_free(tmp); +@@ -2038,6 +2038,7 @@ int main(int argc, char *argv[]) + int c, i; + int columns[ARRAY_SIZE(coldescs)], ncolumns = 0; + int cpu_modifier_specified = 0; ++ size_t setsize; + + static const struct option longopts[] = { + { "all", no_argument, NULL, 'a' }, +@@ -2134,10 +2135,12 @@ int main(int argc, char *argv[]) + + read_basicinfo(desc, mod); + ++ setsize = CPU_ALLOC_SIZE(maxcpus); ++ + for (i = 0; i < desc->ncpuspos; i++) { + /* only consider present CPUs */ + if (desc->present && +- !CPU_ISSET(real_cpu_num(desc, i), desc->present)) ++ !CPU_ISSET_S(real_cpu_num(desc, i), setsize, desc->present)) + continue; + read_topology(desc, i); + read_cache(desc, i); +-- +2.14.4 + diff --git a/SOURCES/0173-chcpu-Fix-maximal-number-of-CPUs.patch b/SOURCES/0173-chcpu-Fix-maximal-number-of-CPUs.patch new file mode 100644 index 0000000..3d27014 --- /dev/null +++ b/SOURCES/0173-chcpu-Fix-maximal-number-of-CPUs.patch @@ -0,0 +1,74 @@ +From 72e09b02e7b4cf3e9e77ef02d5073d9a11f3b314 Mon Sep 17 00:00:00 2001 +From: Stanislav Brabec <sbrabec@suse.cz> +Date: Wed, 9 May 2018 22:13:07 +0200 +Subject: [PATCH 173/173] chcpu: Fix maximal number of CPUs + +chcpu.c mixed maxcpus (number of cpus) and setsize (size of CPU bit +mask). It effectively limits number of CPUs to 1/8 of the supported +amount. + +Signed-off-by: Stanislav Brabec <sbrabec@suse.cz> +Cc: Michael Matz <matz@suse.de> +Cc: Heiko Carstens <heiko.carstens@de.ibm.com> +Upstream: http://github.com/karelzak/util-linux/commit/607274943bfd3d4856b872bc4278b36903fb2182 +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1579439 +--- + sys-utils/chcpu.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/sys-utils/chcpu.c b/sys-utils/chcpu.c +index 7843dfb22..1a0157360 100644 +--- a/sys-utils/chcpu.c ++++ b/sys-utils/chcpu.c +@@ -75,12 +75,12 @@ enum { + */ + static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable) + { +- unsigned int cpu; ++ int cpu; + int online, rc; + int configured = -1; +- size_t fails = 0; ++ int fails = 0; + +- for (cpu = 0; cpu < setsize; cpu++) { ++ for (cpu = 0; cpu < maxcpus; cpu++) { + if (!CPU_ISSET_S(cpu, setsize, cpu_set)) + continue; + if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) { +@@ -132,7 +132,7 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable) + } + } + +- return fails == 0 ? 0 : fails == setsize ? -1 : 1; ++ return fails == 0 ? 0 : fails == maxcpus ? -1 : 1; + } + + static int cpu_rescan(void) +@@ -168,11 +168,11 @@ static int cpu_set_dispatch(int mode) + */ + static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure) + { +- unsigned int cpu; ++ int cpu; + int rc, current; +- size_t fails = 0; ++ int fails = 0; + +- for (cpu = 0; cpu < setsize; cpu++) { ++ for (cpu = 0; cpu < maxcpus; cpu++) { + if (!CPU_ISSET_S(cpu, setsize, cpu_set)) + continue; + if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) { +@@ -217,7 +217,7 @@ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure) + } + } + +- return fails == 0 ? 0 : fails == setsize ? -1 : 1; ++ return fails == 0 ? 0 : fails == maxcpus ? -1 : 1; + } + + static void cpu_parse(char *cpu_string, cpu_set_t *cpu_set, size_t setsize) +-- +2.14.4 + diff --git a/SOURCES/0174-libblkid-minix-Match-minix-superblock-types.patch b/SOURCES/0174-libblkid-minix-Match-minix-superblock-types.patch new file mode 100644 index 0000000..5e42e3b --- /dev/null +++ b/SOURCES/0174-libblkid-minix-Match-minix-superblock-types.patch @@ -0,0 +1,44 @@ +From b4d5cf4819023ce07ecd39729cdd4cde8b59ca35 Mon Sep 17 00:00:00 2001 +From: Nate Clark <nate@neworld.us> +Date: Wed, 4 Jan 2017 15:24:22 -0500 +Subject: [PATCH 174/176] libblkid/minix: Match minix superblock types + +All of the types in the minix super block are unsigned but in +probe_minix they were being treated as signed. This would cause some of +the extra sanity checks to pass on a non minix device. The types were +updated to match the return types of the helper functions in +disk-utils/minix_programs.h + +This can be checked by creating a swap partition with one of these UUIDs +35f1f264-2468-471a-bc85-acc9f4bc04a3 +35f1f264-6824-471a-bc85-acc9f4bc04a3 +35f1f264-2478-471a-bc85-acc9f4bc04a3 +35f1f264-7824-471a-bc85-acc9f4bc04a3 + +Prior to this change they would all be considered minix and swap by +blkid. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1594681 +Upstream: http://github.com/karelzak/util-linux/commit/a9975c1072c4975ec2df958188a80d89cabc6171 +Signed-off-by: Nate Clark <nate@neworld.us> +--- + libblkid/src/superblocks/minix.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c +index 3e80e5b22..a20d51f2c 100644 +--- a/libblkid/src/superblocks/minix.c ++++ b/libblkid/src/superblocks/minix.c +@@ -87,7 +87,8 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag) + + if (version <= 2) { + struct minix_super_block *sb = (struct minix_super_block *) data; +- int zones, ninodes, imaps, zmaps, firstz; ++ unsigned long zones, ninodes, imaps, zmaps; ++ off_t firstz; + + if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0) + return 1; +-- +2.14.4 + diff --git a/SOURCES/0175-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch b/SOURCES/0175-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch new file mode 100644 index 0000000..58dfa33 --- /dev/null +++ b/SOURCES/0175-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch @@ -0,0 +1,44 @@ +From 6eb3a6579768458c13c26958e1cc3fbffe2ada70 Mon Sep 17 00:00:00 2001 +From: Nate Clark <nate@neworld.us> +Date: Wed, 4 Jan 2017 15:21:17 -0500 +Subject: [PATCH 175/176] libblkid/minix: Sanity check superblock s_state for v + 1 and 2 + +Swap devices with specific values in the uuid can look like minix +devices to blkid. Add an extra check to make sure the state of the +filesystem has valid state flags. + +A couple of offending swap uuids include: +35f1f264-137f-471a-bc85-acc9f4bc04a3 +35f1f264-7f13-471a-bc85-acc9f4bc04a3 +35f1f264-138f-471a-bc85-acc9f4bc04a3 +35f1f264-8f13-471a-bc85-acc9f4bc04a3 + +Without this change a swap device with any of those uuids would be +detected as minix and swap by blkid. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1594681 +Upstream: http://github.com/karelzak/util-linux/commit/892553b1a41b449f58462f123eca2bf2c6c56b33 +Signed-off-by: Nate Clark <nate@neworld.us> +--- + libblkid/src/superblocks/minix.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c +index a20d51f2c..4e70fda8f 100644 +--- a/libblkid/src/superblocks/minix.c ++++ b/libblkid/src/superblocks/minix.c +@@ -93,6 +93,10 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag) + if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0) + return 1; + ++ uint16_t state = minix_swab16(swabme, sb->s_state); ++ if ((state & (MINIX_VALID_FS | MINIX_ERROR_FS)) != state) ++ return 1; ++ + zones = version == 2 ? minix_swab32(swabme, sb->s_zones) : + minix_swab16(swabme, sb->s_nzones); + ninodes = minix_swab16(swabme, sb->s_ninodes); +-- +2.14.4 + diff --git a/SOURCES/0176-libblkid-minix-Use-same-checks-for-version-3.patch b/SOURCES/0176-libblkid-minix-Use-same-checks-for-version-3.patch new file mode 100644 index 0000000..577af59 --- /dev/null +++ b/SOURCES/0176-libblkid-minix-Use-same-checks-for-version-3.patch @@ -0,0 +1,90 @@ +From b5d9bfe34e62cf1c57646efa0bd72ecad7d98258 Mon Sep 17 00:00:00 2001 +From: Nate Clark <nate@neworld.us> +Date: Wed, 4 Jan 2017 15:24:32 -0500 +Subject: [PATCH 176/176] libblkid/minix: Use same checks for version 3 + +fsck.minix performs the same sanity checks on all versions of the +superblock. Update the probe to perform the same sanity checks so it is +less likely a different type of filesystem will be identified as minix. + +Upstream: http://github.com/karelzak/util-linux/commit/f82c804869bb8613fa0924e3111b7eb55bb04fcd +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1594681 +Signed-off-by: Nate Clark <nate@neworld.us> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/superblocks/minix.c | 38 +++++++++++++++++++++++--------------- + 1 file changed, 23 insertions(+), 15 deletions(-) + +diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c +index 4e70fda8f..21b3bf8bb 100644 +--- a/libblkid/src/superblocks/minix.c ++++ b/libblkid/src/superblocks/minix.c +@@ -75,6 +75,9 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag) + unsigned char *ext; + const unsigned char *data; + int version = 0, swabme = 0; ++ unsigned long zones, ninodes, imaps, zmaps; ++ off_t firstz; ++ size_t zone_size; + + data = blkid_probe_get_buffer(pr, 1024, + max(sizeof(struct minix_super_block), +@@ -85,14 +88,9 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag) + if (version < 1) + return 1; + ++ + if (version <= 2) { + struct minix_super_block *sb = (struct minix_super_block *) data; +- unsigned long zones, ninodes, imaps, zmaps; +- off_t firstz; +- +- if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0) +- return 1; +- + uint16_t state = minix_swab16(swabme, sb->s_state); + if ((state & (MINIX_VALID_FS | MINIX_ERROR_FS)) != state) + return 1; +@@ -103,20 +101,30 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag) + imaps = minix_swab16(swabme, sb->s_imap_blocks); + zmaps = minix_swab16(swabme, sb->s_zmap_blocks); + firstz = minix_swab16(swabme, sb->s_firstdatazone); +- +- /* sanity checks to be sure that the FS is really minix */ +- if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1) +- return 1; +- if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1) +- return 1; +- ++ zone_size = sb->s_log_zone_size; + } else if (version == 3) { + struct minix3_super_block *sb = (struct minix3_super_block *) data; + +- if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0) +- return 1; ++ zones = minix_swab32(swabme, sb->s_zones); ++ ninodes = minix_swab32(swabme, sb->s_ninodes); ++ imaps = minix_swab16(swabme, sb->s_imap_blocks); ++ zmaps = minix_swab16(swabme, sb->s_zmap_blocks); ++ firstz = minix_swab16(swabme, sb->s_firstdatazone); ++ zone_size = sb->s_log_zone_size; + } + ++ /* sanity checks to be sure that the FS is really minix. ++ * see disk-utils/fsck.minix.c read_superblock ++ */ ++ if (zone_size != 0 || ninodes == 0 || ninodes == UINT32_MAX) ++ return 1; ++ if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1) ++ return 1; ++ if (firstz > (off_t) zones) ++ return 1; ++ if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1) ++ return 1; ++ + /* unfortunately, some parts of ext3 is sometimes possible to + * interpreted as minix superblock. So check for extN magic + * string. (For extN magic string and offsets see ext.c.) +-- +2.14.4 + diff --git a/SOURCES/0177-mount-append-inverting-options-for-mount.-type-on-us.patch b/SOURCES/0177-mount-append-inverting-options-for-mount.-type-on-us.patch new file mode 100644 index 0000000..cf3ba6d --- /dev/null +++ b/SOURCES/0177-mount-append-inverting-options-for-mount.-type-on-us.patch @@ -0,0 +1,41 @@ +From 4b376d1ad1b46408bc59d949c277991a9b5035eb Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 27 Oct 2016 15:30:20 +0200 +Subject: [PATCH 177/178] mount: append inverting options for mount.<type> on + "users" + +If you call mount(8) as root, then we need to append inverting options +(if specified by fstab) for "user" and "users" to /sbin/mount.<type> +command line, because for UID=0 mount.nfs follows command line rather +than the fstab setting. + +This has been originally implemented by commit +a4c0cc75ff9744299f108c259efab1bd30c8007a for the old mount(8). The +same feature is supported by libmount, unfortunately for "user" only. +We need the same also for "users" to be backwardly compatible. + +Addresses: https://github.com/karelzak/util-linux/issues/368 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1618711 +Upstream: http://github.com/karelzak/util-linux/commit/3c4a3de0fcb8f21bffacfd8bdc3d6fbd683c71f5 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libmount/src/context_mount.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c +index 4df2646b0..0f4485592 100644 +--- a/libmount/src/context_mount.c ++++ b/libmount/src/context_mount.c +@@ -321,7 +321,8 @@ static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr) + if (!*optstr) + return -ENOMEM; + +- if (cxt->user_mountflags & MNT_MS_USER) { ++ if ((cxt->user_mountflags & MNT_MS_USER) || ++ (cxt->user_mountflags & MNT_MS_USERS)) { + /* + * This is unnecessary for real user-mounts as mount.<type> + * helpers have to always follow fstab rather than mount +-- +2.14.4 + diff --git a/SOURCES/0178-sulogin-backport-RHEL-8-version.patch b/SOURCES/0178-sulogin-backport-RHEL-8-version.patch new file mode 100644 index 0000000..f33b245 --- /dev/null +++ b/SOURCES/0178-sulogin-backport-RHEL-8-version.patch @@ -0,0 +1,839 @@ +From f720781671c9f5421fb8101dd3bcf7b56efca131 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 20 Aug 2018 14:56:08 +0200 +Subject: [PATCH 178/178] sulogin: backport RHEL-8 version + +The new version is more robust and it does not use mmap()-ed shared +memory between sulogins instances. It also provides some fallbacks for +s390 machines. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1616264 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + configure.ac | 1 + + include/c.h | 4 + + login-utils/sulogin-consoles.c | 116 +++++++++++++------ + login-utils/sulogin.c | 253 +++++++++++++++++++++++++++-------------- + 4 files changed, 252 insertions(+), 122 deletions(-) + +diff --git a/configure.ac b/configure.ac +index d561e01d0..3f07df495 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -218,6 +218,7 @@ AC_CHECK_HEADERS([ \ + sys/prctl.h \ + sys/queue.h \ + sys/resource.h \ ++ sys/sysmacros.h \ + sys/socket.h \ + sys/sockio.h \ + sys/stat.h \ +diff --git a/include/c.h b/include/c.h +index 124035ea5..51d439297 100644 +--- a/include/c.h ++++ b/include/c.h +@@ -19,6 +19,10 @@ + # include <err.h> + #endif + ++#ifdef HAVE_SYS_SYSMACROS_H ++# include <sys/sysmacros.h> /* for major, minor */ ++#endif ++ + #ifndef HAVE_USLEEP + # include <time.h> + #endif +diff --git a/login-utils/sulogin-consoles.c b/login-utils/sulogin-consoles.c +index 07af33a6d..2c0eed3a4 100644 +--- a/login-utils/sulogin-consoles.c ++++ b/login-utils/sulogin-consoles.c +@@ -36,8 +36,9 @@ + # include <linux/serial.h> + # include <linux/major.h> + #endif +-#include <fcntl.h> + #include <dirent.h> ++#include <errno.h> ++#include <fcntl.h> + #include <unistd.h> + + #ifdef USE_SULOGIN_EMERGENCY_MOUNT +@@ -74,7 +75,7 @@ static int consoles_debug; + } while (0) + + static inline void __attribute__ ((__format__ (__printf__, 1, 2))) +-dbgprint(const char *mesg, ...) ++dbgprint(const char * const mesg, ...) + { + va_list ap; + va_start(ap, mesg); +@@ -112,7 +113,7 @@ void emergency_do_mounts(void) + } + + if (stat("/", &rt) != 0) { +- warn("can not get file status of root file system\n"); ++ warn("cannot get file status of root file system\n"); + return; + } + +@@ -150,17 +151,19 @@ void emergency_do_mounts(void) { } + * the caller has to free the result + */ + static __attribute__((__nonnull__)) +-char *oneline(const char *file) ++char *oneline(const char * const file) + { + FILE *fp; + char *ret = NULL; +- size_t len = 0; ++ size_t dummy = 0; ++ ssize_t len; + + DBG(dbgprint("reading %s", file)); + +- if (!(fp = fopen(file, "re"))) ++ if (!(fp = fopen(file, "r" UL_CLOEXECSTR))) + return NULL; +- if (getline(&ret, &len, fp) >= 0) { ++ len = getline(&ret, &dummy, fp); ++ if (len >= 0) { + char *nl; + + if (len) +@@ -179,7 +182,7 @@ char *oneline(const char *file) + * /sys/class/tty, the caller has to free the result. + */ + static __attribute__((__malloc__)) +-char *actattr(const char *tty) ++char *actattr(const char * const tty) + { + char *ret, *path; + +@@ -198,7 +201,7 @@ char *actattr(const char *tty) + * /sys/class/tty. + */ + static +-dev_t devattr(const char *tty) ++dev_t devattr(const char * const tty) + { + dev_t dev = 0; + char *path, *value; +@@ -223,42 +226,77 @@ dev_t devattr(const char *tty) + #endif /* __linux__ */ + + /* +- * Search below /dev for the characer device in `dev_t comparedev' variable. ++ * Search below /dev for the character device in `dev_t comparedev' variable. ++ * Note that realpath(3) is used here to avoid not existent devices due the ++ * strdup(3) used in our canonicalize_path()! + */ + static + #ifdef __GNUC__ + __attribute__((__nonnull__,__malloc__,__hot__)) + #endif +-char* scandev(DIR *dir, dev_t comparedev) ++char* scandev(DIR *dir, const dev_t comparedev) + { ++ char path[PATH_MAX]; + char *name = NULL; +- struct dirent *dent; +- int fd; ++ const struct dirent *dent; ++ int len, fd; + + DBG(dbgprint("scanning /dev for %u:%u", major(comparedev), minor(comparedev))); + ++ /* ++ * Try udev links on character devices first. ++ */ ++ if ((len = snprintf(path, sizeof(path), ++ "/dev/char/%u:%u", major(comparedev), minor(comparedev))) > 0 && ++ (size_t)len < sizeof(path)) { ++ ++ name = realpath(path, NULL); ++ if (name) ++ goto out; ++ } ++ + fd = dirfd(dir); + rewinddir(dir); + while ((dent = readdir(dir))) { +- char path[PATH_MAX]; + struct stat st; ++ ++#ifdef _DIRENT_HAVE_D_TYPE ++ if (dent->d_type != DT_UNKNOWN && dent->d_type != DT_CHR) ++ continue; ++#endif + if (fstatat(fd, dent->d_name, &st, 0) < 0) + continue; + if (!S_ISCHR(st.st_mode)) + continue; + if (comparedev != st.st_rdev) + continue; +- if ((size_t)snprintf(path, sizeof(path), "/dev/%s", dent->d_name) >= sizeof(path)) ++ if ((len = snprintf(path, sizeof(path), "/dev/%s", dent->d_name)) < 0 || ++ (size_t)len >= sizeof(path)) + continue; +-#ifdef USE_SULOGIN_EMERGENCY_MOUNT +- if (emergency_flags & MNT_DEVTMPFS) +- mknod(path, S_IFCHR|S_IRUSR|S_IWUSR, comparedev); +-#endif + +- name = canonicalize_path(path); +- break; ++ name = realpath(path, NULL); ++ if (name) ++ goto out; + } + ++#ifdef USE_SULOGIN_EMERGENCY_MOUNT ++ /* ++ * There was no /dev mounted hence and no device was found hence we create our own. ++ */ ++ if (!name && (emergency_flags & MNT_DEVTMPFS)) { ++ ++ if ((len = snprintf(path, sizeof(path), ++ "/dev/tmp-%u:%u", major(comparedev), minor(comparedev))) < 0 || ++ (size_t)len >= sizeof(path)) ++ goto out; ++ ++ if (mknod(path, S_IFCHR|S_IRUSR|S_IWUSR, comparedev) < 0 && errno != EEXIST) ++ goto out; ++ ++ name = realpath(path, NULL); ++ } ++#endif ++out: + return name; + } + +@@ -273,12 +311,12 @@ char* scandev(DIR *dir, dev_t comparedev) + */ + static + #ifdef __GNUC__ +-__attribute__((__nonnull__,__hot__)) ++__attribute__((__hot__)) + #endif +-int append_console(struct list_head *consoles, const char *name) ++int append_console(struct list_head *consoles, const char * const name) + { + struct console *restrict tail; +- struct console *last = NULL; ++ const struct console *last = NULL; + + DBG(dbgprint("appenging %s", name)); + +@@ -300,7 +338,7 @@ int append_console(struct list_head *consoles, const char *name) + tail->flags = 0; + tail->fd = -1; + tail->id = last ? last->id + 1 : 0; +- tail->pid = 0; ++ tail->pid = -1; + memset(&tail->tio, 0, sizeof(tail->tio)); + + return 0; +@@ -319,11 +357,11 @@ static int detect_consoles_from_proc(struct list_head *consoles) + char fbuf[16 + 1]; + DIR *dir = NULL; + FILE *fc = NULL; +- int maj, min, rc = 1; ++ int maj, min, rc = 1, matches; + + DBG(dbgprint("trying /proc")); + +- fc = fopen("/proc/consoles", "re"); ++ fc = fopen("/proc/consoles", "r" UL_CLOEXECSTR); + if (!fc) { + rc = 2; + goto done; +@@ -332,10 +370,12 @@ static int detect_consoles_from_proc(struct list_head *consoles) + if (!dir) + goto done; + +- while (fscanf(fc, "%*s %*s (%16[^)]) %d:%d", fbuf, &maj, &min) == 3) { ++ while ((matches = fscanf(fc, "%*s %*s (%16[^)]) %d:%d", fbuf, &maj, &min)) >= 1) { + char *name; + dev_t comparedev; + ++ if (matches != 3) ++ continue; + if (!strchr(fbuf, 'E')) + continue; + comparedev = makedev(maj, min); +@@ -509,7 +549,7 @@ done: + + #ifdef TIOCGDEV + static int detect_consoles_from_tiocgdev(struct list_head *consoles, +- int fallback, ++ const int fallback, + const char *device) + { + unsigned int devnum; +@@ -579,7 +619,7 @@ done: + * Returns 1 if stdout and stderr should be reconnected and 0 + * otherwise or less than zero on error. + */ +-int detect_consoles(const char *device, int fallback, struct list_head *consoles) ++int detect_consoles(const char *device, const int fallback, struct list_head *consoles) + { + int fd, reconnect = 0, rc; + dev_t comparedev = 0; +@@ -587,7 +627,7 @@ int detect_consoles(const char *device, int fallback, struct list_head *consoles + consoles_debug = getenv("CONSOLES_DEBUG") ? 1 : 0; + + if (!device || !*device) +- fd = dup(fallback); ++ fd = fallback >= 0 ? dup(fallback) : - 1; + else { + fd = open(device, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC); + reconnect = 1; +@@ -602,6 +642,14 @@ int detect_consoles(const char *device, int fallback, struct list_head *consoles + struct stat st; + #ifdef TIOCGDEV + unsigned int devnum; ++#endif ++#ifdef __GNU__ ++ /* ++ * The Hurd always gives st_rdev as 0, which causes this ++ * method to select the first terminal it finds. ++ */ ++ close(fd); ++ goto fallback; + #endif + DBG(dbgprint("trying device/fallback file descriptor")); + +@@ -670,7 +718,7 @@ int detect_consoles(const char *device, int fallback, struct list_head *consoles + #ifdef __linux__ + console: + /* +- * Detection of devices used for Linux system consolei using ++ * Detection of devices used for Linux system console using + * the /proc/consoles API with kernel 2.6.38 and higher. + */ + rc = detect_consoles_from_proc(consoles); +@@ -754,8 +802,7 @@ int main(int argc, char *argv[]) + { + char *name = NULL; + int fd, re; +- LIST_HEAD(consoles); +- struct list_head *p; ++ struct list_head *p, consoles; + + if (argc == 2) { + name = argv[1]; +@@ -768,6 +815,7 @@ int main(int argc, char *argv[]) + if (!name) + errx(EXIT_FAILURE, "usage: %s [<tty>]\n", program_invocation_short_name); + ++ INIT_LIST_HEAD(&consoles); + re = detect_consoles(name, fd, &consoles); + + list_for_each(p, &consoles) { +diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c +index dd73a1c50..4620ed2da 100644 +--- a/login-utils/sulogin.c ++++ b/login-utils/sulogin.c +@@ -56,8 +56,12 @@ + + #include "c.h" + #include "closestream.h" ++#include "env.h" + #include "nls.h" + #include "pathnames.h" ++#ifdef USE_PLYMOUTH_SUPPORT ++# include "plymouth-ctrl.h" ++#endif + #include "strutils.h" + #include "ttyutils.h" + #include "sulogin-consoles.h" +@@ -66,13 +70,12 @@ + static unsigned int timeout; + static int profile; + static volatile uint32_t openfd; /* Remember higher file descriptors */ +-static volatile uint32_t *usemask; + +-struct sigaction saved_sigint; +-struct sigaction saved_sigtstp; +-struct sigaction saved_sigquit; +-struct sigaction saved_sighup; +-struct sigaction saved_sigchld; ++static struct sigaction saved_sigint; ++static struct sigaction saved_sigtstp; ++static struct sigaction saved_sigquit; ++static struct sigaction saved_sighup; ++static struct sigaction saved_sigchld; + + static volatile sig_atomic_t alarm_rised; + static volatile sig_atomic_t sigchild; +@@ -81,7 +84,12 @@ static volatile sig_atomic_t sigchild; + # define IUCLC 0 + #endif + +-static int locked_account_password(const char *passwd) ++#ifndef WEXITED ++# warning "WEXITED is missing, sulogin may not work as expected" ++# define WEXITED 0 ++#endif ++ ++static int locked_account_password(const char * const passwd) + { + if (passwd && (*passwd == '*' || *passwd == '!')) + return 1; +@@ -93,10 +101,29 @@ static int locked_account_password(const char *passwd) + */ + static void tcinit(struct console *con) + { +- int mode = 0, flags = 0; ++ int flags = 0, mode = 0; + struct termios *tio = &con->tio; +- int fd = con->fd; +- ++ const int fd = con->fd; ++#ifdef USE_PLYMOUTH_SUPPORT ++ struct termios lock; ++ int i = (plymouth_command(MAGIC_PING)) ? PLYMOUTH_TERMIOS_FLAGS_DELAY : 0; ++ if (i) ++ plymouth_command(MAGIC_QUIT); ++ while (i-- > 0) { ++ /* ++ * With plymouth the termios flags become changed after this ++ * function had changed the termios. ++ */ ++ memset(&lock, 0, sizeof(struct termios)); ++ if (ioctl(fd, TIOCGLCKTRMIOS, &lock) < 0) ++ break; ++ if (!lock.c_iflag && !lock.c_oflag && !lock.c_cflag && !lock.c_lflag) ++ break; ++ sleep(1); ++ } ++ memset(&lock, 0, sizeof(struct termios)); ++ ioctl(fd, TIOCSLCKTRMIOS, &lock); ++#endif + errno = 0; + + if (tcgetattr(fd, tio) < 0) { +@@ -189,20 +216,23 @@ setattr: + */ + static void tcfinal(struct console *con) + { +- struct termios *tio; +- int fd; ++ struct termios *tio = &con->tio; ++ const int fd = con->fd; + + if ((con->flags & CON_SERIAL) == 0) { +- setenv("TERM", "linux", 1); ++ xsetenv("TERM", "linux", 1); + return; + } +- if (con->flags & CON_NOTTY) ++ if (con->flags & CON_NOTTY) { ++ xsetenv("TERM", "dumb", 1); + return; ++ } + +- setenv("TERM", "vt102", 1); +- tio = &con->tio; +- fd = con->fd; +- ++#if defined (__s390__) || defined (__s390x__) ++ xsetenv("TERM", "dumb", 1); ++#else ++ xsetenv("TERM", "vt102", 1); ++#endif + tio->c_iflag |= (IXON | IXOFF); + tio->c_lflag |= (ICANON | ISIG | ECHO|ECHOE|ECHOK|ECHOKE); + tio->c_oflag |= OPOST; +@@ -237,11 +267,11 @@ static void tcfinal(struct console *con) + break; + case 1: /* odd parity */ + tio->c_cflag |= PARODD; +- /* fall through */ ++ /* fallthrough */ + case 2: /* even parity */ + tio->c_cflag |= PARENB; + tio->c_iflag |= (INPCK | ISTRIP); +- /* fall through */ ++ /* fallthrough */ + case (1 | 2): /* no parity bit */ + tio->c_cflag &= ~CSIZE; + tio->c_cflag |= CS7; +@@ -466,7 +496,7 @@ static struct passwd *getrootpwent(int try_manually) + warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD); + *pwd.pw_passwd = '\0'; + } +- /* locked accont passwords are valid too */ ++ /* locked account passwords are valid too */ + if (!locked_account_password(pwd.pw_passwd) && !valid(pwd.pw_passwd)) { + warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD); + *pwd.pw_passwd = '\0'; +@@ -524,22 +554,17 @@ err: + */ + static void setup(struct console *con) + { +- pid_t pid, pgrp, ppgrp, ttypgrp; +- int fd; ++ int fd = con->fd; ++ const pid_t pid = getpid(), pgrp = getpgid(0), ppgrp = ++ getpgid(getppid()), ttypgrp = tcgetpgrp(fd); + + if (con->flags & CON_NOTTY) + return; +- fd = con->fd; + + /* + * Only go through this trouble if the new + * tty doesn't fall in this process group. + */ +- pid = getpid(); +- pgrp = getpgid(0); +- ppgrp = getpgid(getppid()); +- ttypgrp = tcgetpgrp(fd); +- + if (pgrp != ttypgrp && ppgrp != ttypgrp) { + if (pid != getsid(0)) { + if (pid == getpgid(0)) +@@ -575,21 +600,20 @@ static void setup(struct console *con) + * Ask for the password. Note that there is no default timeout as we normally + * skip this during boot. + */ +-static char *getpasswd(struct console *con) ++static const char *getpasswd(struct console *con) + { + struct sigaction sa; + struct termios tty; + static char pass[128], *ptr; + struct chardata *cp; +- char *ret = pass; ++ const char *ret = pass; + unsigned char tc; + char c, ascval; + int eightbit; +- int fd; ++ const int fd = con->fd; + + if (con->flags & CON_NOTTY) + goto out; +- fd = con->fd; + cp = &con->cp; + tty = con->tio; + +@@ -597,6 +621,7 @@ static char *getpasswd(struct console *con) + tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|ISIG); + tc = (tcsetattr(fd, TCSAFLUSH, &tty) == 0); + ++ sigemptyset(&sa.sa_mask); + sa.sa_handler = alrm_handler; + sa.sa_flags = 0; + sigaction(SIGALRM, &sa, NULL); +@@ -615,7 +640,7 @@ static char *getpasswd(struct console *con) + ret = NULL; + goto quit; + } +- usleep(1000); ++ usleep(250000); + continue; + } + ret = (char*)0; +@@ -627,7 +652,7 @@ static char *getpasswd(struct console *con) + case ENOENT: + break; + default: +- warn(_("%s: read failed"), con->tty); ++ warn(_("cannot read %s"), con->tty); + break; + } + goto quit; +@@ -694,8 +719,8 @@ static void sushell(struct passwd *pwd) + { + char shell[PATH_MAX]; + char home[PATH_MAX]; +- char *p; +- char *su_shell; ++ char const *p; ++ char const *su_shell; + + /* + * Set directory and shell. +@@ -731,16 +756,16 @@ static void sushell(struct passwd *pwd) + if (getcwd(home, sizeof(home)) == NULL) + strcpy(home, "/"); + +- setenv("HOME", home, 1); +- setenv("LOGNAME", "root", 1); +- setenv("USER", "root", 1); ++ xsetenv("HOME", home, 1); ++ xsetenv("LOGNAME", "root", 1); ++ xsetenv("USER", "root", 1); + if (!profile) +- setenv("SHLVL","0",1); ++ xsetenv("SHLVL","0",1); + + /* + * Try to execute a shell. + */ +- setenv("SHELL", su_shell, 1); ++ xsetenv("SHELL", su_shell, 1); + unmask_signal(SIGINT, &saved_sigint); + unmask_signal(SIGTSTP, &saved_sigtstp); + unmask_signal(SIGQUIT, &saved_sigquit); +@@ -765,17 +790,21 @@ static void sushell(struct passwd *pwd) + execl(su_shell, shell, NULL); + warn(_("failed to execute %s"), su_shell); + +- setenv("SHELL", "/bin/sh", 1); ++ xsetenv("SHELL", "/bin/sh", 1); + execl("/bin/sh", profile ? "-sh" : "sh", NULL); + warn(_("failed to execute %s"), "/bin/sh"); + } + +-static void usage(FILE *out) ++static void usage(void) + { ++ FILE *out = stdout; + fputs(USAGE_HEADER, out); + fprintf(out, _( + " %s [options] [tty device]\n"), program_invocation_short_name); + ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Single-user login.\n"), out); ++ + fputs(USAGE_OPTIONS, out); + fputs(_(" -p, --login-shell start a login shell\n" + " -t, --timeout <seconds> max time to wait for a password (default: no limit)\n" +@@ -783,32 +812,35 @@ static void usage(FILE *out) + out); + + fputs(USAGE_SEPARATOR, out); +- fputs(USAGE_HELP, out); +- fputs(USAGE_VERSION, out); +- fprintf(out, USAGE_MAN_TAIL("sulogin(8)")); ++ printf(USAGE_HELP_OPTIONS(26)); ++ printf(USAGE_MAN_TAIL("sulogin(8)")); + } + + int main(int argc, char **argv) + { +- LIST_HEAD(consoles); +- struct list_head *ptr; ++ struct list_head *ptr, consoles; + struct console *con; + char *tty = NULL; + struct passwd *pwd; +- int c, status = 0; +- int reconnect = 0; ++ const struct timespec sigwait = { .tv_sec = 0, .tv_nsec = 50000000 }; ++ siginfo_t status = { 0 }; ++ sigset_t set; ++ int c, reconnect = 0; + int opt_e = 0; ++ int wait = 0; + pid_t pid; + + static const struct option longopts[] = { +- { "login-shell", 0, 0, 'p' }, +- { "timeout", 1, 0, 't' }, +- { "force", 0, 0, 'e' }, +- { "help", 0, 0, 'h' }, +- { "version", 0, 0, 'V' }, +- { NULL, 0, 0, 0 } ++ { "login-shell", no_argument, NULL, 'p' }, ++ { "timeout", required_argument, NULL, 't' }, ++ { "force", no_argument, NULL, 'e' }, ++ { "help", no_argument, NULL, 'h' }, ++ { "version", no_argument, NULL, 'V' }, ++ { NULL, 0, NULL, 0 } + }; + ++ INIT_LIST_HEAD(&consoles); ++ + /* + * If we are init we need to set up a own session. + */ +@@ -840,17 +872,16 @@ int main(int argc, char **argv) + printf(UTIL_LINUX_VERSION); + return EXIT_SUCCESS; + case 'h': +- usage(stdout); ++ usage(); + return EXIT_SUCCESS; + default: +- usage(stderr); +- /* Do not exit! */ ++ /* Do not exit! getopt prints a warning. */ + break; + } + } + + if (geteuid() != 0) +- errx(EXIT_FAILURE, _("only root can run this program.")); ++ errx(EXIT_FAILURE, _("only superuser can run this program")); + + mask_signal(SIGQUIT, SIG_IGN, &saved_sigquit); + mask_signal(SIGTSTP, SIG_IGN, &saved_sigtstp); +@@ -877,7 +908,7 @@ int main(int argc, char **argv) + reconnect = detect_consoles(tty, STDIN_FILENO, &consoles); + + /* +- * If previous stdin was not the speified tty and therefore reconnected ++ * If previous stdin was not the specified tty and therefore reconnected + * to the specified tty also reconnect stdout and stderr. + */ + if (reconnect) { +@@ -900,7 +931,7 @@ int main(int argc, char **argv) + * Get the root password. + */ + if ((pwd = getrootpwent(opt_e)) == NULL) { +- warnx(_("cannot open password database.")); ++ warnx(_("cannot open password database")); + sleep(2); + return EXIT_FAILURE; + } +@@ -923,9 +954,6 @@ int main(int argc, char **argv) + tcinit(con); + } + ptr = (&consoles)->next; +- usemask = (uint32_t*) mmap(NULL, sizeof(uint32_t), +- PROT_READ|PROT_WRITE, +- MAP_ANONYMOUS|MAP_SHARED, -1, 0); + + if (ptr->next == &consoles) { + con = list_entry(ptr, struct console, entry); +@@ -942,7 +970,6 @@ int main(int argc, char **argv) + switch ((con->pid = fork())) { + case 0: + mask_signal(SIGCHLD, SIG_DFL, NULL); +- /* fall through */ + nofork: + setup(con); + while (1) { +@@ -971,9 +998,7 @@ int main(int argc, char **argv) + } + + if (doshell) { +- *usemask |= (1<<con->id); + sushell(pwd); +- *usemask &= ~(1<<con->id); + failed++; + } + +@@ -982,7 +1007,7 @@ int main(int argc, char **argv) + mask_signal(SIGINT, SIG_IGN, &saved_sigint); + + if (failed) { +- fprintf(stderr, _("Can not execute su shell\n\n")); ++ fprintf(stderr, _("cannot execute su shell\n\n")); + break; + } + fprintf(stderr, _("Login incorrect\n\n")); +@@ -997,7 +1022,7 @@ int main(int argc, char **argv) + exit(0); + case -1: + warn(_("fork failed")); +- /* fall through */ ++ /* fallthrough */ + default: + break; + } +@@ -1006,28 +1031,80 @@ int main(int argc, char **argv) + + } while (ptr != &consoles); + +- while ((pid = wait(&status))) { +- if (errno == ECHILD) ++ do { ++ int ret; ++ ++ status.si_pid = 0; ++ ret = waitid(P_ALL, 0, &status, WEXITED); ++ ++ if (ret == 0) + break; +- if (pid < 0) +- continue; +- list_for_each(ptr, &consoles) { +- con = list_entry(ptr, struct console, entry); +- if (con->pid == pid) { +- *usemask &= ~(1<<con->id); ++ if (ret < 0) { ++ if (errno == ECHILD) ++ break; ++ if (errno == EINTR) + continue; +- } +- if (kill(con->pid, 0) < 0) { +- *usemask &= ~(1<<con->id); ++ } ++ ++ errx(EXIT_FAILURE, _("cannot wait on su shell\n\n")); ++ ++ } while (1); ++ ++ list_for_each(ptr, &consoles) { ++ con = list_entry(ptr, struct console, entry); ++ ++ if (con->fd < 0) ++ continue; ++ if (con->pid < 0) ++ continue; ++ if (con->pid == status.si_pid) ++ con->pid = -1; ++ else { ++ kill(con->pid, SIGTERM); ++ wait++; ++ } ++ } ++ ++ sigemptyset(&set); ++ sigaddset(&set, SIGCHLD); ++ ++ do { ++ int signum, ret; ++ ++ if (!wait) ++ break; ++ ++ status.si_pid = 0; ++ ret = waitid(P_ALL, 0, &status, WEXITED|WNOHANG); ++ ++ if (ret < 0) { ++ if (errno == ECHILD) ++ break; ++ if (errno == EINTR) + continue; ++ } ++ ++ if (!ret && status.si_pid > 0) { ++ list_for_each(ptr, &consoles) { ++ con = list_entry(ptr, struct console, entry); ++ ++ if (con->fd < 0) ++ continue; ++ if (con->pid < 0) ++ continue; ++ if (con->pid == status.si_pid) { ++ con->pid = -1; ++ wait--; ++ } + } +- if (*usemask & (1<<con->id)) +- continue; +- kill(con->pid, SIGHUP); +- usleep(5000); +- kill(con->pid, SIGKILL); ++ continue; + } +- } ++ ++ signum = sigtimedwait(&set, NULL, &sigwait); ++ if (signum != SIGCHLD && signum < 0 && errno == EAGAIN) ++ break; ++ ++ } while (1); + + mask_signal(SIGCHLD, SIG_DFL, NULL); + return EXIT_SUCCESS; +-- +2.14.4 + diff --git a/SOURCES/0179-agetty-backport-RHEL-8-version.patch b/SOURCES/0179-agetty-backport-RHEL-8-version.patch new file mode 100644 index 0000000..4a2bfad --- /dev/null +++ b/SOURCES/0179-agetty-backport-RHEL-8-version.patch @@ -0,0 +1,1912 @@ +From ea152b4e9656945f17d18bf4e5ece91e3e57ebb1 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 29 Jan 2019 11:57:55 +0100 +Subject: [PATCH] agetty: backport RHEL-8 version + +The code is identical to RHEL-8.0, except: + + * disabled ISSUEDIR_SUPPORT + * disabled AGETTY_RELOAD + * removed \e and \e{name} feature + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1664752 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + include/pathnames.h | 5 +- + term-utils/agetty.c | 1141 +++++++++++++++++++++++++++++++------------ + 2 files changed, 839 insertions(+), 307 deletions(-) + +diff --git a/include/pathnames.h b/include/pathnames.h +index fa4bddbad..ec5eef918 100644 +--- a/include/pathnames.h ++++ b/include/pathnames.h +@@ -64,9 +64,10 @@ + + /* used in term-utils/agetty.c */ + #define _PATH_ISSUE "/etc/issue" +-#define _PATH_OS_RELEASE "/etc/os-release" ++#define _PATH_ISSUEDIR _PATH_ISSUE ".d" ++#define _PATH_OS_RELEASE_ETC "/etc/os-release" ++#define _PATH_OS_RELEASE_USR "/usr/lib/os-release" + #define _PATH_NUMLOCK_ON _PATH_LOCALSTATEDIR "/numlock-on" +- + #define _PATH_LOGINDEFS "/etc/login.defs" + + /* used in misc-utils/look.c */ +diff --git a/term-utils/agetty.c b/term-utils/agetty.c +index 948d77246..c3c7ab504 100644 +--- a/term-utils/agetty.c ++++ b/term-utils/agetty.c +@@ -10,6 +10,7 @@ + * + * This program is freely distributable. + */ ++ + #include <stdio.h> + #include <unistd.h> + #include <stdlib.h> +@@ -20,19 +21,21 @@ + #include <sys/ioctl.h> + #include <sys/types.h> + #include <sys/stat.h> ++#include <sys/wait.h> + #include <fcntl.h> + #include <stdarg.h> + #include <ctype.h> +-#include <utmp.h> ++#include <utmpx.h> + #include <getopt.h> + #include <time.h> +-#include <sys/file.h> + #include <sys/socket.h> + #include <langinfo.h> + #include <grp.h> + #include <arpa/inet.h> + #include <netdb.h> + #include <ifaddrs.h> ++#include <net/if.h> ++#include <sys/utsname.h> + + #include "strutils.h" + #include "all-io.h" +@@ -41,14 +44,37 @@ + #include "c.h" + #include "widechar.h" + #include "ttyutils.h" ++#include "env.h" ++ ++#ifdef USE_PLYMOUTH_SUPPORT ++# include "plymouth-ctrl.h" ++#endif ++ ++#ifdef HAVE_SYS_PARAM_H ++# include <sys/param.h> ++#endif ++ ++#if defined(__FreeBSD_kernel__) ++# include <pty.h> ++# ifdef HAVE_UTMP_H ++# include <utmp.h> ++# endif ++# ifdef HAVE_LIBUTIL_H ++# include <libutil.h> ++# endif ++#endif + + #ifdef __linux__ + # include <sys/kd.h> +-# include <sys/param.h> + # define USE_SYSLOG + # ifndef DEFAULT_VCTERM + # define DEFAULT_VCTERM "linux" + # endif ++# if defined (__s390__) || defined (__s390x__) ++# define DEFAULT_TTYS0 "dumb" ++# define DEFAULT_TTY32 "ibm327x" ++# define DEFAULT_TTYS1 "vt220" ++# endif + # ifndef DEFAULT_STERM + # define DEFAULT_STERM "vt102" + # endif +@@ -69,6 +95,10 @@ + # endif + #endif + ++#ifdef __FreeBSD_kernel__ ++#define USE_SYSLOG ++#endif ++ + /* If USE_SYSLOG is undefined all diagnostics go to /dev/console. */ + #ifdef USE_SYSLOG + # include <syslog.h> +@@ -86,9 +116,9 @@ + /* + * Things you may want to modify. + * +- * If ISSUE is not defined, agetty will never display the contents of the +- * /etc/issue file. You will not want to spit out large "issue" files at the +- * wrong baud rate. Relevant for System V only. ++ * If ISSUE_SUPPORT is not defined, agetty will never display the contents of ++ * the /etc/issue file. You will not want to spit out large "issue" files at ++ * the wrong baud rate. Relevant for System V only. + * + * You may disagree with the default line-editing etc. characters defined + * below. Note, however, that DEL cannot be used for interrupt generation +@@ -97,14 +127,26 @@ + + /* Displayed before the login prompt. */ + #ifdef SYSV_STYLE +-# define ISSUE _PATH_ISSUE +-# include <sys/utsname.h> ++# define ISSUE_SUPPORT + #endif + + /* Login prompt. */ + #define LOGIN "login: " + #define LOGIN_ARGV_MAX 16 /* Numbers of args for login */ + ++/* ++ * agetty --reload ++ */ ++#ifdef AGETTY_RELOAD ++# include <sys/inotify.h> ++# include <linux/netlink.h> ++# include <linux/rtnetlink.h> ++# define AGETTY_RELOAD_FILENAME "/run/agetty.reload" /* trigger file */ ++# define AGETTY_RELOAD_FDNONE -2 /* uninitialized fd */ ++static int inotify_fd = AGETTY_RELOAD_FDNONE; ++static int netlink_fd = AGETTY_RELOAD_FDNONE; ++#endif ++ + /* + * When multiple baud rates are specified on the command line, the first one + * we will try is the first one specified. +@@ -116,7 +158,7 @@ + + struct options { + int flags; /* toggle switches, see below */ +- int timeout; /* time-out period */ ++ unsigned int timeout; /* time-out period */ + char *autolog; /* login the user automatically */ + char *chdir; /* Chdir before the login */ + char *chroot; /* Chroot before the login */ +@@ -126,11 +168,11 @@ struct options { + char *vcline; /* line of virtual console */ + char *term; /* terminal type */ + char *initstring; /* modem init string */ +- char *issue; /* alternative issue file */ ++ char *issue; /* alternative issue file or directory */ + char *erasechars; /* string with erase chars */ + char *killchars; /* string with kill chars */ + char *osrelease; /* /etc/os-release data */ +- int delay; /* Sleep seconds before prompt */ ++ unsigned int delay; /* Sleep seconds before prompt */ + int nice; /* Run login with this priority */ + int numspeed; /* number of baud rates to try */ + int clocal; /* CLOCAL_MODE_* */ +@@ -145,12 +187,12 @@ enum { + }; + + #define F_PARSE (1<<0) /* process modem status messages */ +-#define F_ISSUE (1<<1) /* display /etc/issue */ ++#define F_ISSUE (1<<1) /* display /etc/issue or /etc/issue.d */ + #define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */ + + #define F_INITSTRING (1<<4) /* initstring is set */ + #define F_WAITCRLF (1<<5) /* wait for CR or LF */ +-#define F_CUSTISSUE (1<<6) /* give alternative issue file */ ++ + #define F_NOPROMPT (1<<7) /* do not ask for login name! */ + #define F_LCUC (1<<8) /* support for *LCUC stty modes */ + #define F_KEEPSPEED (1<<9) /* follow baud rate from kernel */ +@@ -189,16 +231,14 @@ static const struct Speedtab speedtab[] = { + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, +-#ifdef B19200 ++#ifdef B19200 + {19200, B19200}, +-#endif +-#ifdef B38400 +- {38400, B38400}, +-#endif +-#ifdef EXTA ++#elif defined(EXTA) + {19200, EXTA}, + #endif +-#ifdef EXTB ++#ifdef B38400 ++ {38400, B38400}, ++#elif defined(EXTB) + {38400, EXTB}, + #endif + #ifdef B57600 +@@ -209,6 +249,42 @@ static const struct Speedtab speedtab[] = { + #endif + #ifdef B230400 + {230400, B230400}, ++#endif ++#ifdef B460800 ++ {460800, B460800}, ++#endif ++#ifdef B500000 ++ {500000, B500000}, ++#endif ++#ifdef B576000 ++ {576000, B576000}, ++#endif ++#ifdef B921600 ++ {921600, B921600}, ++#endif ++#ifdef B1000000 ++ {1000000, B1000000}, ++#endif ++#ifdef B1152000 ++ {1152000, B1152000}, ++#endif ++#ifdef B1500000 ++ {1500000, B1500000}, ++#endif ++#ifdef B2000000 ++ {2000000, B2000000}, ++#endif ++#ifdef B2500000 ++ {2500000, B2500000}, ++#endif ++#ifdef B3000000 ++ {3000000, B3000000}, ++#endif ++#ifdef B3500000 ++ {3500000, B3500000}, ++#endif ++#ifdef B4000000 ++ {4000000, B4000000}, + #endif + {0, 0}, + }; +@@ -221,6 +297,7 @@ static void open_tty(char *tty, struct termios *tp, struct options *op); + static void termio_init(struct options *op, struct termios *tp); + static void reset_vc (const struct options *op, struct termios *tp); + static void auto_baud(struct termios *tp); ++static void list_speeds(void); + static void output_special_char (unsigned char c, struct options *op, + struct termios *tp, FILE *fp); + static void do_prompt(struct options *op, struct termios *tp); +@@ -231,7 +308,8 @@ static void termio_final(struct options *op, + struct termios *tp, struct chardata *cp); + static int caps_lock(char *s); + static speed_t bcode(char *s); +-static void usage(FILE * out) __attribute__((__noreturn__)); ++static void usage(void) __attribute__((__noreturn__)); ++static void exit_slowly(int code) __attribute__((__noreturn__)); + static void log_err(const char *, ...) __attribute__((__noreturn__)) + __attribute__((__format__(printf, 1, 2))); + static void log_warn (const char *, ...) +@@ -239,13 +317,16 @@ static void log_warn (const char *, ...) + static ssize_t append(char *dest, size_t len, const char *sep, const char *src); + static void check_username (const char* nm); + static void login_options_to_argv(char *argv[], int *argc, char *str, char *username); ++static void reload_agettys(void); ++static void print_issue_file(struct options *op, struct termios *tp); + + /* Fake hostname for ut_host specified on command line. */ + static char *fakehost; + + #ifdef DEBUGGING ++# include "closestream.h" + # ifndef DEBUG_OUTPUT +-# define DEBUG_OUTPUT "/dev/ttyp0" ++# define DEBUG_OUTPUT "/dev/tty10" + # endif + # define debug(s) do { fprintf(dbf,s); fflush(dbf); } while (0) + FILE *dbf; +@@ -261,8 +342,7 @@ int main(int argc, char **argv) + struct options options = { + .flags = F_ISSUE, /* show /etc/issue (SYSV_STYLE) */ + .login = _PATH_LOGIN, /* default login program */ +- .tty = "tty1", /* default tty line */ +- .issue = ISSUE /* default issue file */ ++ .tty = "tty1" /* default tty line */ + }; + char *login_argv[LOGIN_ARGV_MAX + 1]; + int login_argc = 0; +@@ -283,8 +363,12 @@ int main(int argc, char **argv) + + #ifdef DEBUGGING + dbf = fopen(DEBUG_OUTPUT, "w"); +- for (int i = 1; i < argc; i++) ++ for (int i = 1; i < argc; i++) { ++ if (i > 1) ++ debug(" "); + debug(argv[i]); ++ } ++ debug("\n"); + #endif /* DEBUGGING */ + + /* Parse command-line arguments. */ +@@ -311,6 +395,13 @@ int main(int argc, char **argv) + sigaction(SIGHUP, &sa_hup, NULL); + + tcsetpgrp(STDIN_FILENO, getpid()); ++ ++ /* Default is to follow the current line speed and then default to 9600 */ ++ if ((options.flags & F_VCONSOLE) == 0 && options.numspeed == 0) { ++ options.speeds[options.numspeed++] = bcode("9600"); ++ options.flags |= F_KEEPSPEED; ++ } ++ + /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ + debug("calling termio_init\n"); + termio_init(&options, &termios); +@@ -337,7 +428,7 @@ int main(int argc, char **argv) + + /* Set the optional timer. */ + if (options.timeout) +- alarm((unsigned) options.timeout); ++ alarm(options.timeout); + + /* Optionally wait for CR or LF before writing /etc/issue */ + if (serial_tty_option(&options, F_WAITCRLF)) { +@@ -362,7 +453,9 @@ int main(int argc, char **argv) + username = options.autolog; + } + +- if ((options.flags & F_NOPROMPT) == 0) { ++ if (options.flags & F_NOPROMPT) { /* --skip-login */ ++ print_issue_file(&options, &termios); ++ } else { /* regular (auto)login */ + if (options.autolog) { + /* Autologin prompt */ + do_prompt(&options, &termios); +@@ -372,7 +465,7 @@ int main(int argc, char **argv) + debug("reading login name\n"); + while ((username = + get_logname(&options, &termios, &chardata)) == NULL) +- if ((options.flags & F_VCONSOLE) == 0) ++ if ((options.flags & F_VCONSOLE) == 0 && options.numspeed) + next_speed(&options, &termios); + } + } +@@ -403,9 +496,12 @@ int main(int argc, char **argv) + login_options_to_argv(login_argv, &login_argc, + options.logopt, username); + } else { +- if (fakehost && (options.flags & F_REMOTE)) { +- login_argv[login_argc++] = "-h"; +- login_argv[login_argc++] = fakehost; ++ if (options.flags & F_REMOTE) { ++ if (fakehost) { ++ login_argv[login_argc++] = "-h"; ++ login_argv[login_argc++] = fakehost; ++ } else if (options.flags & F_NOHOSTNAME) ++ login_argv[login_argc++] = "-H"; + } + if (username) { + if (options.autolog) +@@ -418,25 +514,18 @@ int main(int argc, char **argv) + + login_argv[login_argc] = NULL; /* last login argv */ + +- if (options.chroot) { +- if (chroot(options.chroot) < 0) +- log_err(_("%s: can't change root directory %s: %m"), +- options.tty, options.chroot); +- } +- if (options.chdir) { +- if (chdir(options.chdir) < 0) +- log_err(_("%s: can't change working directory %s: %m"), +- options.tty, options.chdir); +- } +- if (options.nice) { +- if (nice(options.nice) < 0) +- log_warn(_("%s: can't change process priority: %m"), +- options.tty); +- } +- if (options.osrelease) +- free(options.osrelease); ++ if (options.chroot && chroot(options.chroot) < 0) ++ log_err(_("%s: can't change root directory %s: %m"), ++ options.tty, options.chroot); ++ if (options.chdir && chdir(options.chdir) < 0) ++ log_err(_("%s: can't change working directory %s: %m"), ++ options.tty, options.chdir); ++ if (options.nice && nice(options.nice) < 0) ++ log_warn(_("%s: can't change process priority: %m"), ++ options.tty); ++ ++ free(options.osrelease); + #ifdef DEBUGGING +- fprintf(dbf, "read %c\n", ch); + if (close_stream(dbf) != 0) + log_err("write failed: %s", DEBUG_OUTPUT); + #endif +@@ -477,7 +566,7 @@ static char *replace_u(char *str, char *username) + log_err(_("failed to allocate memory: %m")); + + if (p != str) { +- /* copy chars befor \u */ ++ /* copy chars before \u */ + memcpy(tp, str, p - str); + tp += p - str; + } +@@ -527,6 +616,55 @@ static void login_options_to_argv(char *argv[], int *argc, + *argc = i; + } + ++static void output_version(void) ++{ ++ static const char *features[] = { ++#ifdef DEBUGGING ++ "debug", ++#endif ++#ifdef CRTSCTS ++ "flow control", ++#endif ++#ifdef KDGKBLED ++ "hints", ++#endif ++#ifdef ISSUE_SUPPORT ++ "issue", ++#endif ++#ifdef ISSUEDIR_SUPPORT ++ "issue.d", ++#endif ++#ifdef KDGKBMODE ++ "keyboard mode", ++#endif ++#ifdef USE_PLYMOUTH_SUPPORT ++ "plymouth", ++#endif ++#ifdef AGETTY_RELOAD ++ "reload", ++#endif ++#ifdef USE_SYSLOG ++ "syslog", ++#endif ++#ifdef HAVE_WIDECHAR ++ "widechar", ++#endif ++ NULL ++ }; ++ unsigned int i; ++ ++ printf( _("%s from %s"), program_invocation_short_name, PACKAGE_STRING); ++ fputs(" (", stdout); ++ for (i = 0; features[i]; i++) { ++ if (0 < i) ++ fputs(", ", stdout); ++ printf("%s", features[i]); ++ } ++ fputs(")\n", stdout); ++} ++ ++#define is_speed(str) (strlen((str)) == strspn((str), "0123456789,")) ++ + /* Parse command-line arguments. */ + static void parse_args(int argc, char **argv, struct options *op) + { +@@ -540,46 +678,50 @@ static void parse_args(int argc, char **argv, struct options *op) + HELP_OPTION, + ERASE_CHARS_OPTION, + KILL_CHARS_OPTION, ++ RELOAD_OPTION, ++ LIST_SPEEDS_OPTION, + }; + const struct option longopts[] = { +- { "8bits", no_argument, 0, '8' }, +- { "autologin", required_argument, 0, 'a' }, +- { "noreset", no_argument, 0, 'c' }, +- { "chdir", required_argument, 0, 'C' }, +- { "delay", required_argument, 0, 'd' }, +- { "remote", no_argument, 0, 'E' }, +- { "issue-file", required_argument, 0, 'f' }, +- { "flow-control", no_argument, 0, 'h' }, +- { "host", required_argument, 0, 'H' }, +- { "noissue", no_argument, 0, 'i' }, +- { "init-string", required_argument, 0, 'I' }, +- { "noclear", no_argument, 0, 'J' }, +- { "login-program", required_argument, 0, 'l' }, +- { "local-line", optional_argument, 0, 'L' }, +- { "extract-baud", no_argument, 0, 'm' }, +- { "skip-login", no_argument, 0, 'n' }, +- { "nonewline", no_argument, 0, 'N' }, +- { "login-options", required_argument, 0, 'o' }, +- { "login-pause", no_argument, 0, 'p' }, +- { "nice", required_argument, 0, 'P' }, +- { "chroot", required_argument, 0, 'r' }, +- { "hangup", no_argument, 0, 'R' }, +- { "keep-baud", no_argument, 0, 's' }, +- { "timeout", required_argument, 0, 't' }, +- { "detect-case", no_argument, 0, 'U' }, +- { "wait-cr", no_argument, 0, 'w' }, +- { "nohints", no_argument, 0, NOHINTS_OPTION }, +- { "nohostname", no_argument, 0, NOHOSTNAME_OPTION }, +- { "long-hostname", no_argument, 0, LONGHOSTNAME_OPTION }, +- { "version", no_argument, 0, VERSION_OPTION }, +- { "help", no_argument, 0, HELP_OPTION }, +- { "erase-chars", required_argument, 0, ERASE_CHARS_OPTION }, +- { "kill-chars", required_argument, 0, KILL_CHARS_OPTION }, +- { NULL, 0, 0, 0 } ++ { "8bits", no_argument, NULL, '8' }, ++ { "autologin", required_argument, NULL, 'a' }, ++ { "noreset", no_argument, NULL, 'c' }, ++ { "chdir", required_argument, NULL, 'C' }, ++ { "delay", required_argument, NULL, 'd' }, ++ { "remote", no_argument, NULL, 'E' }, ++ { "issue-file", required_argument, NULL, 'f' }, ++ { "flow-control", no_argument, NULL, 'h' }, ++ { "host", required_argument, NULL, 'H' }, ++ { "noissue", no_argument, NULL, 'i' }, ++ { "init-string", required_argument, NULL, 'I' }, ++ { "noclear", no_argument, NULL, 'J' }, ++ { "login-program", required_argument, NULL, 'l' }, ++ { "local-line", optional_argument, NULL, 'L' }, ++ { "extract-baud", no_argument, NULL, 'm' }, ++ { "list-speeds", no_argument, NULL, LIST_SPEEDS_OPTION }, ++ { "skip-login", no_argument, NULL, 'n' }, ++ { "nonewline", no_argument, NULL, 'N' }, ++ { "login-options", required_argument, NULL, 'o' }, ++ { "login-pause", no_argument, NULL, 'p' }, ++ { "nice", required_argument, NULL, 'P' }, ++ { "chroot", required_argument, NULL, 'r' }, ++ { "hangup", no_argument, NULL, 'R' }, ++ { "keep-baud", no_argument, NULL, 's' }, ++ { "timeout", required_argument, NULL, 't' }, ++ { "detect-case", no_argument, NULL, 'U' }, ++ { "wait-cr", no_argument, NULL, 'w' }, ++ { "nohints", no_argument, NULL, NOHINTS_OPTION }, ++ { "nohostname", no_argument, NULL, NOHOSTNAME_OPTION }, ++ { "long-hostname", no_argument, NULL, LONGHOSTNAME_OPTION }, ++ { "reload", no_argument, NULL, RELOAD_OPTION }, ++ { "version", no_argument, NULL, VERSION_OPTION }, ++ { "help", no_argument, NULL, HELP_OPTION }, ++ { "erase-chars", required_argument, NULL, ERASE_CHARS_OPTION }, ++ { "kill-chars", required_argument, NULL, KILL_CHARS_OPTION }, ++ { NULL, 0, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, +- "8a:cC:d:Ef:hH:iI:Jl:LmnNo:pP:r:Rst:Uw", longopts, ++ "8a:cC:d:Ef:hH:iI:Jl:L::mnNo:pP:r:Rst:Uw", longopts, + NULL)) != -1) { + switch (c) { + case '8': +@@ -595,13 +737,12 @@ static void parse_args(int argc, char **argv, struct options *op) + op->chdir = optarg; + break; + case 'd': +- op->delay = atoi(optarg); ++ op->delay = strtou32_or_err(optarg, _("invalid delay argument")); + break; + case 'E': + op->flags |= F_REMOTE; + break; + case 'f': +- op->flags |= F_CUSTISSUE; + op->issue = optarg; + break; + case 'h': +@@ -634,7 +775,7 @@ static void parse_args(int argc, char **argv, struct options *op) + else if (strcmp(optarg, "=auto") == 0) + op->clocal = CLOCAL_MODE_AUTO; + else +- log_err(_("unssuported --local-line mode argument")); ++ log_err(_("invalid argument of --local-line")); + } + break; + case 'm': +@@ -643,6 +784,9 @@ static void parse_args(int argc, char **argv, struct options *op) + case 'n': + op->flags |= F_NOPROMPT; + break; ++ case 'N': ++ op->flags |= F_NONL; ++ break; + case 'o': + op->logopt = optarg; + break; +@@ -650,7 +794,7 @@ static void parse_args(int argc, char **argv, struct options *op) + op->flags |= F_LOGINPAUSE; + break; + case 'P': +- op->nice = atoi(optarg); ++ op->nice = strtos32_or_err(optarg, _("invalid nice argument")); + break; + case 'r': + op->chroot = optarg; +@@ -662,8 +806,7 @@ static void parse_args(int argc, char **argv, struct options *op) + op->flags |= F_KEEPSPEED; + break; + case 't': +- if ((op->timeout = atoi(optarg)) <= 0) +- log_err(_("bad timeout value: %s"), optarg); ++ op->timeout = strtou32_or_err(optarg, _("invalid timeout argument")); + break; + case 'U': + op->flags |= F_LCUC; +@@ -686,14 +829,19 @@ static void parse_args(int argc, char **argv, struct options *op) + case KILL_CHARS_OPTION: + op->killchars = optarg; + break; ++ case RELOAD_OPTION: ++ reload_agettys(); ++ exit(EXIT_SUCCESS); ++ case LIST_SPEEDS_OPTION: ++ list_speeds(); ++ exit(EXIT_SUCCESS); + case VERSION_OPTION: +- printf(_("%s from %s\n"), program_invocation_short_name, +- PACKAGE_STRING); ++ output_version(); + exit(EXIT_SUCCESS); + case HELP_OPTION: +- usage(stdout); ++ usage(); + default: +- usage(stderr); ++ errtryhelp(EXIT_FAILURE); + } + } + +@@ -701,26 +849,26 @@ static void parse_args(int argc, char **argv, struct options *op) + + if (argc < optind + 1) { + log_warn(_("not enough arguments")); +- usage(stderr); ++ errx(EXIT_FAILURE, _("not enough arguments")); + } + + /* Accept "tty", "baudrate tty", and "tty baudrate". */ +- if ('0' <= argv[optind][0] && argv[optind][0] <= '9') { ++ if (is_speed(argv[optind])) { + /* Assume BSD style speed. */ + parse_speeds(op, argv[optind++]); + if (argc < optind + 1) { +- warn(_("not enough arguments")); +- usage(stderr); ++ log_warn(_("not enough arguments")); ++ errx(EXIT_FAILURE, _("not enough arguments")); + } + op->tty = argv[optind++]; + } else { + op->tty = argv[optind++]; + if (argc > optind) { +- char *v = argv[optind++]; +- if ('0' <= *v && *v <= '9') ++ char *v = argv[optind]; ++ if (is_speed(v)) { + parse_speeds(op, v); +- else +- op->speeds[op->numspeed++] = bcode("9600"); ++ optind++; ++ } + } + } + +@@ -732,45 +880,6 @@ static void parse_args(int argc, char **argv, struct options *op) + if (argc > optind && argv[optind]) + op->term = argv[optind]; + +-#ifdef DO_DEVFS_FIDDLING +- /* +- * Some devfs junk, following Goswin Brederlow: +- * turn ttyS<n> into tts/<n> +- * turn tty<n> into vc/<n> +- * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=72241 +- */ +- if (op->tty && strlen(op->tty) < 90) { +- char dev_name[100]; +- struct stat st; +- +- if (strncmp(op->tty, "ttyS", 4) == 0) { +- strcpy(dev_name, "/dev/"); +- strcat(dev_name, op->tty); +- if (stat(dev_name, &st) < 0) { +- strcpy(dev_name, "/dev/tts/"); +- strcat(dev_name, op->tty + 4); +- if (stat(dev_name, &st) == 0) { +- op->tty = strdup(dev_name + 5); +- if (!op->tty) +- log_err(_("failed to allocate memory: %m")); +- } +- } +- } else if (strncmp(op->tty, "tty", 3) == 0) { +- strcpy(dev_name, "/dev/"); +- strncat(dev_name, op->tty, 90); +- if (stat(dev_name, &st) < 0) { +- strcpy(dev_name, "/dev/vc/"); +- strcat(dev_name, op->tty + 3); +- if (stat(dev_name, &st) == 0) { +- op->tty = strdup(dev_name + 5); +- if (!op->tty) +- log_err(_("failed to allocate memory: %m")); +- } +- } +- } +- } +-#endif /* DO_DEVFS_FIDDLING */ +- + debug("exiting parseargs\n"); + } + +@@ -778,15 +887,20 @@ static void parse_args(int argc, char **argv, struct options *op) + static void parse_speeds(struct options *op, char *arg) + { + char *cp; ++ char *str = strdup(arg); ++ ++ if (!str) ++ log_err(_("failed to allocate memory: %m")); + +- debug("entered parse_speeds\n"); +- for (cp = strtok(arg, ","); cp != NULL; cp = strtok((char *)0, ",")) { ++ debug("entered parse_speeds:\n"); ++ for (cp = strtok(str, ","); cp != NULL; cp = strtok((char *)0, ",")) { + if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0) + log_err(_("bad speed: %s"), cp); + if (op->numspeed >= MAX_SPEED) + log_err(_("too many alternate speeds")); + } + debug("exiting parsespeeds\n"); ++ free(str); + } + + #ifdef SYSV_STYLE +@@ -794,13 +908,13 @@ static void parse_speeds(struct options *op, char *arg) + /* Update our utmp entry. */ + static void update_utmp(struct options *op) + { +- struct utmp ut; ++ struct utmpx ut; + time_t t; + pid_t pid = getpid(); + pid_t sid = getsid(0); + char *vcline = op->vcline; + char *line = op->tty; +- struct utmp *utp; ++ struct utmpx *utp; + + /* + * The utmp file holds miscellaneous information about things started by +@@ -810,8 +924,8 @@ static void update_utmp(struct options *op) + * utmp file can be opened for update, and if we are able to find our + * entry in the utmp file. + */ +- utmpname(_PATH_UTMP); +- setutent(); ++ utmpxname(_PATH_UTMP); ++ setutxent(); + + /* + * Find my pid in utmp. +@@ -822,7 +936,7 @@ static void update_utmp(struct options *op) + * FIXME: The present code is taken from login.c, so if this is changed, + * maybe login has to be changed as well (is this true?). + */ +- while ((utp = getutent())) ++ while ((utp = getutxent())) + if (utp->ut_pid == pid + && utp->ut_type >= INIT_PROCESS + && utp->ut_type <= DEAD_PROCESS) +@@ -852,33 +966,15 @@ static void update_utmp(struct options *op) + if (fakehost) + strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host)); + time(&t); +- ut.ut_time = t; ++ ut.ut_tv.tv_sec = t; + ut.ut_type = LOGIN_PROCESS; + ut.ut_pid = pid; + ut.ut_session = sid; + +- pututline(&ut); +- endutent(); ++ pututxline(&ut); ++ endutxent(); + +- { +-#ifdef HAVE_UPDWTMP +- updwtmp(_PATH_WTMP, &ut); +-#else +- int ut_fd; +- int lf; +- +- if ((lf = open(_PATH_WTMPLOCK, O_CREAT | O_WRONLY, 0660)) >= 0) { +- flock(lf, LOCK_EX); +- if ((ut_fd = +- open(_PATH_WTMP, O_APPEND | O_WRONLY)) >= 0) { +- write_all(ut_fd, &ut, sizeof(ut)); +- close(ut_fd); +- } +- flock(lf, LOCK_UN); +- close(lf); +- } +-#endif /* HAVE_UPDWTMP */ +- } ++ updwtmpx(_PATH_WTMP, &ut); + } + + #endif /* SYSV_STYLE */ +@@ -888,6 +984,9 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) + { + const pid_t pid = getpid(); + int closed = 0; ++#ifndef KDGKBMODE ++ int serial; ++#endif + + /* Set up new standard input, unless we are given an already opened port. */ + +@@ -903,8 +1002,12 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) + if ((gr = getgrnam("tty"))) + gid = gr->gr_gid; + +- if (((len = snprintf(buf, sizeof(buf), "/dev/%s", tty)) >= +- (int)sizeof(buf)) || (len < 0)) ++ len = snprintf(buf, sizeof(buf), "/dev/%s", tty); ++ if (len < 0 || (size_t)len >= sizeof(buf)) ++ log_err(_("/dev/%s: cannot open as standard input: %m"), tty); ++ ++ /* Open the tty as standard input. */ ++ if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0) + log_err(_("/dev/%s: cannot open as standard input: %m"), tty); + + /* +@@ -913,24 +1016,20 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) + * Linux login(1) will change tty permissions. Use root owner and group + * with permission -rw------- for the period between getty and login. + */ +- if (chown(buf, 0, gid) || chmod(buf, (gid ? 0620 : 0600))) { ++ if (fchown(fd, 0, gid) || fchmod(fd, (gid ? 0620 : 0600))) { + if (errno == EROFS) + log_warn("%s: %m", buf); + else + log_err("%s: %m", buf); + } + +- /* Open the tty as standard input. */ +- if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0) +- log_err(_("/dev/%s: cannot open as standard input: %m"), tty); +- + /* Sanity checks... */ +- if (!isatty(fd)) +- log_err(_("/dev/%s: not a character device"), tty); + if (fstat(fd, &st) < 0) + log_err("%s: %m", buf); + if ((st.st_mode & S_IFMT) != S_IFCHR) + log_err(_("/dev/%s: not a character device"), tty); ++ if (!isatty(fd)) ++ log_err(_("/dev/%s: not a tty"), tty); + + if (((tid = tcgetsid(fd)) < 0) || (pid != tid)) { + if (ioctl(fd, TIOCSCTTY, 1) == -1) +@@ -946,7 +1045,7 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) + debug("TIOCNOTTY ioctl failed\n"); + + /* +- * Let's close all file decriptors before vhangup ++ * Let's close all file descriptors before vhangup + * https://lkml.org/lkml/2012/6/5/145 + */ + close(fd); +@@ -1015,22 +1114,66 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) + if (tcgetattr(STDIN_FILENO, tp) < 0) + log_err(_("%s: failed to get terminal attributes: %m"), tty); + ++#if defined (__s390__) || defined (__s390x__) ++ if (!op->term) { ++ /* ++ * Special terminal on first serial line on a S/390(x) which ++ * is due legacy reasons a block terminal of type 3270 or ++ * higher. Whereas the second serial line on a S/390(x) is ++ * a real character terminal which is compatible with VT220. ++ */ ++ if (strcmp(op->tty, "ttyS0") == 0) /* linux/drivers/s390/char/con3215.c */ ++ op->term = DEFAULT_TTYS0; ++ else if (strncmp(op->tty, "3270/tty", 8) == 0) /* linux/drivers/s390/char/con3270.c */ ++ op->term = DEFAULT_TTY32; ++ else if (strcmp(op->tty, "ttyS1") == 0) /* linux/drivers/s390/char/sclp_vt220.c */ ++ op->term = DEFAULT_TTYS1; ++ } ++#endif ++ ++#if defined(__FreeBSD_kernel__) ++ login_tty (0); ++#endif ++ + /* + * Detect if this is a virtual console or serial/modem line. + * In case of a virtual console the ioctl KDGKBMODE succeeds + * whereas on other lines it will fails. + */ +- if (ioctl(STDIN_FILENO, KDGKBMODE, &op->kbmode) == 0) { ++#ifdef KDGKBMODE ++ if (ioctl(STDIN_FILENO, KDGKBMODE, &op->kbmode) == 0) ++#else ++ if (ioctl(STDIN_FILENO, TIOCMGET, &serial) < 0 && (errno == EINVAL)) ++#endif ++ { + op->flags |= F_VCONSOLE; + if (!op->term) + op->term = DEFAULT_VCTERM; + } else { ++#ifdef K_RAW + op->kbmode = K_RAW; ++#endif + if (!op->term) + op->term = DEFAULT_STERM; + } + +- setenv("TERM", op->term, 1); ++ if (setenv("TERM", op->term, 1) != 0) ++ log_err(_("failed to set the %s environment variable"), "TERM"); ++} ++ ++/* Initialize termios settings. */ ++static void termio_clear(int fd) ++{ ++ /* ++ * Do not write a full reset (ESC c) because this destroys ++ * the unicode mode again if the terminal was in unicode ++ * mode. Also it clears the CONSOLE_MAGIC features which ++ * are required for some languages/console-fonts. ++ * Just put the cursor to the home position (ESC [ H), ++ * erase everything below the cursor (ESC [ J), and set the ++ * scrolling region to the full window (ESC [ r) ++ */ ++ write_all(fd, "\033[r\033[H\033[J", 9); + } + + /* Initialize termios settings. */ +@@ -1038,6 +1181,28 @@ static void termio_init(struct options *op, struct termios *tp) + { + speed_t ispeed, ospeed; + struct winsize ws; ++#ifdef USE_PLYMOUTH_SUPPORT ++ struct termios lock; ++ int i = (plymouth_command(MAGIC_PING) == 0) ? PLYMOUTH_TERMIOS_FLAGS_DELAY : 0; ++ if (i) ++ plymouth_command(MAGIC_QUIT); ++ while (i-- > 0) { ++ /* ++ * Even with TTYReset=no it seems with systemd or plymouth ++ * the termios flags become changed from under the first ++ * agetty on a serial system console as the flags are locked. ++ */ ++ memset(&lock, 0, sizeof(struct termios)); ++ if (ioctl(STDIN_FILENO, TIOCGLCKTRMIOS, &lock) < 0) ++ break; ++ if (!lock.c_iflag && !lock.c_oflag && !lock.c_cflag && !lock.c_lflag) ++ break; ++ debug("termios locked\n"); ++ sleep(1); ++ } ++ memset(&lock, 0, sizeof(struct termios)); ++ ioctl(STDIN_FILENO, TIOCSLCKTRMIOS, &lock); ++#endif + + if (op->flags & F_VCONSOLE) { + #if defined(IUTF8) && defined(KDGKBMODE) +@@ -1063,22 +1228,16 @@ static void termio_init(struct options *op, struct termios *tp) + if ((tp->c_cflag & (CS8|PARODD|PARENB)) == CS8) + op->flags |= F_EIGHTBITS; + +- if ((op->flags & F_NOCLEAR) == 0) { +- /* +- * Do not write a full reset (ESC c) because this destroys +- * the unicode mode again if the terminal was in unicode +- * mode. Also it clears the CONSOLE_MAGIC features which +- * are required for some languages/console-fonts. +- * Just put the cursor to the home position (ESC [ H), +- * erase everything below the cursor (ESC [ J), and set the +- * scrolling region to the full window (ESC [ r) +- */ +- write_all(STDOUT_FILENO, "\033[r\033[H\033[J", 9); +- } ++ if ((op->flags & F_NOCLEAR) == 0) ++ termio_clear(STDOUT_FILENO); + return; + } + +- if (op->flags & F_KEEPSPEED) { ++ /* ++ * Serial line ++ */ ++ ++ if (op->flags & F_KEEPSPEED || !op->numspeed) { + /* Save the original setting. */ + ispeed = cfgetispeed(tp); + ospeed = cfgetospeed(tp); +@@ -1097,9 +1256,6 @@ static void termio_init(struct options *op, struct termios *tp) + * later on. + */ + +- /* Flush input and output queues, important for modems! */ +- tcflush(STDIN_FILENO, TCIOFLUSH); +- + /* The defaul is set c_iflag in termio_final() according to chardata. + * Unfortunately, the chardata are not set according to the serial line + * if --autolog is enabled. In this case we do not read from the line +@@ -1124,7 +1280,7 @@ static void termio_init(struct options *op, struct termios *tp) + + /* + * Note that the speed is stored in the c_cflag termios field, so we have +- * set the speed always when the cflag se reseted. ++ * set the speed always when the cflag is reset. + */ + cfsetispeed(tp, ispeed); + cfsetospeed(tp, ospeed); +@@ -1150,15 +1306,10 @@ static void termio_init(struct options *op, struct termios *tp) + + /* Check for terminal size and if not found set default */ + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) { +- int set = 0; +- if (ws.ws_row == 0) { ++ if (ws.ws_row == 0) + ws.ws_row = 24; +- set++; +- } +- if (ws.ws_col == 0) { ++ if (ws.ws_col == 0) + ws.ws_col = 80; +- set++; +- } + if (ioctl(STDIN_FILENO, TIOCSWINSZ, &ws)) + debug("TIOCSWINSZ ioctl failed\n"); + } +@@ -1168,8 +1319,11 @@ static void termio_init(struct options *op, struct termios *tp) + if (op->flags & F_RTSCTS) + tp->c_cflag |= CRTSCTS; + #endif ++ /* Flush input and output queues, important for modems! */ ++ tcflush(STDIN_FILENO, TCIOFLUSH); + +- tcsetattr(STDIN_FILENO, TCSANOW, tp); ++ if (tcsetattr(STDIN_FILENO, TCSANOW, tp)) ++ log_warn(_("setting terminal attributes failed: %m")); + + /* Go to blocking input even in local mode. */ + fcntl(STDIN_FILENO, F_SETFL, +@@ -1190,6 +1344,10 @@ static void reset_vc(const struct options *op, struct termios *tp) + + if (tcsetattr(STDIN_FILENO, TCSADRAIN, tp)) + log_warn(_("setting terminal attributes failed: %m")); ++ ++ /* Go to blocking input even in local mode. */ ++ fcntl(STDIN_FILENO, F_SETFL, ++ fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK); + } + + /* Extract baud rate from modem status message. */ +@@ -1273,7 +1431,7 @@ static char *xgetdomainname(void) + { + #ifdef HAVE_GETDOMAINNAME + char *name; +- size_t sz = get_hostname_max() + 1; ++ const size_t sz = get_hostname_max() + 1; + + name = malloc(sizeof(char) * sz); + if (!name) +@@ -1285,8 +1443,9 @@ static char *xgetdomainname(void) + } + name[sz - 1] = '\0'; + return name; +-#endif ++#else + return NULL; ++#endif + } + + +@@ -1299,10 +1458,13 @@ static char *read_os_release(struct options *op, const char *varname) + + /* read the file only once */ + if (!op->osrelease) { +- fd = open(_PATH_OS_RELEASE, O_RDONLY); ++ fd = open(_PATH_OS_RELEASE_ETC, O_RDONLY); + if (fd == -1) { +- log_warn(_("cannot open: %s: %m"), _PATH_OS_RELEASE); +- return NULL; ++ fd = open(_PATH_OS_RELEASE_USR, O_RDONLY); ++ if (fd == -1) { ++ log_warn(_("cannot open os-release file")); ++ return NULL; ++ } + } + + if (fstat(fd, &st) < 0 || st.st_size > 4 * 1024 * 1024) +@@ -1359,8 +1521,7 @@ static char *read_os_release(struct options *op, const char *varname) + } + break; + } +- if (ret) +- free(ret); ++ free(ret); + ret = strdup(p); + if (!ret) + log_err(_("failed to allocate memory: %m")); +@@ -1373,20 +1534,251 @@ done: + return ret; + } + +-/* Show login prompt, optionally preceded by /etc/issue contents. */ +-static void do_prompt(struct options *op, struct termios *tp) ++#ifdef AGETTY_RELOAD ++static void open_netlink(void) + { +-#ifdef ISSUE +- FILE *fd; +-#endif /* ISSUE */ ++ struct sockaddr_nl addr = { 0, }; ++ int sock; + ++ if (netlink_fd != AGETTY_RELOAD_FDNONE) ++ return; ++ ++ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); ++ if (sock >= 0) { ++ addr.nl_family = AF_NETLINK; ++ addr.nl_pid = getpid(); ++ addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; ++ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) ++ close(sock); ++ else ++ netlink_fd = sock; ++ } ++} ++ ++static int process_netlink_msg(int *changed) ++{ ++ char buf[4096]; ++ struct sockaddr_nl snl; ++ struct nlmsghdr *h; ++ int rc; ++ ++ struct iovec iov = { ++ .iov_base = buf, ++ .iov_len = sizeof(buf) ++ }; ++ struct msghdr msg = { ++ .msg_name = &snl, ++ .msg_namelen = sizeof(snl), ++ .msg_iov = &iov, ++ .msg_iovlen = 1, ++ .msg_control = NULL, ++ .msg_controllen = 0, ++ .msg_flags = 0 ++ }; ++ ++ rc = recvmsg(netlink_fd, &msg, MSG_DONTWAIT); ++ if (rc < 0) { ++ if (errno == EWOULDBLOCK || errno == EAGAIN) ++ return 0; ++ ++ /* Failure, just stop listening for changes */ ++ close(netlink_fd); ++ netlink_fd = AGETTY_RELOAD_FDNONE; ++ return 0; ++ } ++ ++ for (h = (struct nlmsghdr *)buf; NLMSG_OK(h, (unsigned int)rc); h = NLMSG_NEXT(h, rc)) { ++ if (h->nlmsg_type == NLMSG_DONE || ++ h->nlmsg_type == NLMSG_ERROR) { ++ close(netlink_fd); ++ netlink_fd = AGETTY_RELOAD_FDNONE; ++ return 0; ++ } ++ ++ *changed = 1; ++ break; ++ } ++ ++ return 1; ++} ++ ++static int process_netlink(void) ++{ ++ int changed = 0; ++ while (process_netlink_msg(&changed)); ++ return changed; ++} ++ ++static int wait_for_term_input(int fd) ++{ ++ char buffer[sizeof(struct inotify_event) + NAME_MAX + 1]; ++ fd_set rfds; ++ ++ if (inotify_fd == AGETTY_RELOAD_FDNONE) { ++ /* make sure the reload trigger file exists */ ++ int reload_fd = open(AGETTY_RELOAD_FILENAME, ++ O_CREAT|O_CLOEXEC|O_RDONLY, ++ S_IRUSR|S_IWUSR); ++ ++ /* initialize reload trigger inotify stuff */ ++ if (reload_fd >= 0) { ++ inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); ++ if (inotify_fd > 0) ++ inotify_add_watch(inotify_fd, AGETTY_RELOAD_FILENAME, ++ IN_ATTRIB | IN_MODIFY); ++ ++ close(reload_fd); ++ } else ++ log_warn(_("failed to create reload file: %s: %m"), ++ AGETTY_RELOAD_FILENAME); ++ } ++ ++ while (1) { ++ int nfds = fd; ++ ++ FD_ZERO(&rfds); ++ FD_SET(fd, &rfds); ++ ++ if (inotify_fd >= 0) { ++ FD_SET(inotify_fd, &rfds); ++ nfds = max(nfds, inotify_fd); ++ } ++ if (netlink_fd >= 0) { ++ FD_SET(netlink_fd, &rfds); ++ nfds = max(nfds, netlink_fd); ++ } ++ ++ /* If waiting fails, just fall through, presumably reading input will fail */ ++ if (select(nfds + 1, &rfds, NULL, NULL, NULL) < 0) ++ return 1; ++ ++ if (FD_ISSET(fd, &rfds)) { ++ return 1; ++ ++ } else if (netlink_fd >= 0 && FD_ISSET(netlink_fd, &rfds)) { ++ if (!process_netlink()) ++ continue; ++ ++ /* Just drain the inotify buffer */ ++ } else if (inotify_fd >= 0 && FD_ISSET(inotify_fd, &rfds)) { ++ while (read(inotify_fd, buffer, sizeof (buffer)) > 0); ++ } ++ ++ return 0; ++ } ++} ++#endif /* AGETTY_RELOAD */ ++ ++#ifdef ISSUEDIR_SUPPORT ++static int issuedir_filter(const struct dirent *d) ++{ ++ size_t namesz; ++ ++#ifdef _DIRENT_HAVE_D_TYPE ++ if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG && ++ d->d_type != DT_LNK) ++ return 0; ++#endif ++ if (*d->d_name == '.') ++ return 0; ++ ++ namesz = strlen(d->d_name); ++ if (!namesz || namesz < ISSUEDIR_EXTSIZ + 1 || ++ strcmp(d->d_name + (namesz - ISSUEDIR_EXTSIZ), ISSUEDIR_EXT)) ++ return 0; ++ ++ /* Accept this */ ++ return 1; ++} ++ ++static FILE *issuedir_next_file(int dd, struct dirent **namelist, int nfiles, int *n) ++{ ++ while (*n < nfiles) { ++ struct dirent *d = namelist[*n]; ++ struct stat st; ++ FILE *f; ++ ++ (*n)++; ++ ++ if (fstatat(dd, d->d_name, &st, 0) || ++ !S_ISREG(st.st_mode)) ++ continue; ++ ++ f = fopen_at(dd, d->d_name, O_RDONLY|O_CLOEXEC, "r" UL_CLOEXECSTR); ++ if (f) ++ return f; ++ } ++ return NULL; ++} ++ ++#endif /* ISSUEDIR_SUPPORT */ ++ ++#ifndef ISSUE_SUPPORT ++static void print_issue_file(struct options *op, struct termios *tp __attribute__((__unused__))) ++{ ++ if ((op->flags & F_NONL) == 0) { ++ /* Issue not in use, start with a new line. */ ++ write_all(STDOUT_FILENO, "\r\n", 2); ++ } ++} ++#else /* ISSUE_SUPPORT */ ++ ++static void print_issue_file(struct options *op, struct termios *tp) ++{ ++ const char *filename, *dirname = NULL; ++ FILE *f = NULL; ++#ifdef ISSUEDIR_SUPPORT ++ int dd = -1, nfiles = 0, i; ++ struct dirent **namelist = NULL; ++#endif + if ((op->flags & F_NONL) == 0) { + /* Issue not in use, start with a new line. */ + write_all(STDOUT_FILENO, "\r\n", 2); + } + +-#ifdef ISSUE +- if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) { ++ if (!(op->flags & F_ISSUE)) ++ return; ++ ++ /* ++ * The custom issue file or directory specified by: agetty -f <path>. ++ * Note that nothing is printed if the file/dir does not exist. ++ */ ++ filename = op->issue; ++ if (filename) { ++ struct stat st; ++ ++ if (stat(filename, &st) < 0) ++ return; ++ if (S_ISDIR(st.st_mode)) { ++ dirname = filename; ++ filename = NULL; ++ } ++ } else { ++ /* The default /etc/issue and optional /etc/issue.d directory ++ * as extension to the file. The /etc/issue.d directory is ++ * ignored if there is no /etc/issue file. The file may be ++ * empty or symlink. ++ */ ++ if (access(_PATH_ISSUE, F_OK|R_OK) != 0) ++ return; ++ filename = _PATH_ISSUE; ++ dirname = _PATH_ISSUEDIR; ++ } ++ ++#ifdef ISSUEDIR_SUPPORT ++ if (dirname) { ++ dd = open(dirname, O_RDONLY|O_CLOEXEC|O_DIRECTORY); ++ if (dd >= 0) ++ nfiles = scandirat(dd, ".", &namelist, issuedir_filter, versionsort); ++ if (nfiles <= 0) ++ dirname = NULL; ++ } ++ i = 0; ++#endif ++ if (filename) ++ f = fopen(filename, "r"); ++ ++ if (f || dirname) { + int c, oflag = tp->c_oflag; /* Save current setting. */ + + if ((op->flags & F_VCONSOLE) == 0) { +@@ -1395,12 +1787,23 @@ static void do_prompt(struct options *op, struct termios *tp) + tcsetattr(STDIN_FILENO, TCSADRAIN, tp); + } + +- while ((c = getc(fd)) != EOF) { +- if (c == '\\') +- output_special_char(getc(fd), op, tp, fd); +- else +- putchar(c); +- } ++ do { ++#ifdef ISSUEDIR_SUPPORT ++ if (!f && i < nfiles) ++ f = issuedir_next_file(dd, namelist, nfiles, &i); ++#endif ++ if (!f) ++ break; ++ while ((c = getc(f)) != EOF) { ++ if (c == '\\') ++ output_special_char(getc(f), op, tp, f); ++ else ++ putchar(c); ++ } ++ fclose(f); ++ f = NULL; ++ } while (dirname); ++ + fflush(stdout); + + if ((op->flags & F_VCONSOLE) == 0) { +@@ -1409,11 +1812,36 @@ static void do_prompt(struct options *op, struct termios *tp) + /* Wait till output is gone. */ + tcsetattr(STDIN_FILENO, TCSADRAIN, tp); + } +- fclose(fd); + } +-#endif /* ISSUE */ ++ ++#ifdef ISSUEDIR_SUPPORT ++ for (i = 0; i < nfiles; i++) ++ free(namelist[i]); ++ free(namelist); ++ if (dd >= 0) ++ close(dd); ++#endif ++} ++#endif /* ISSUE_SUPPORT */ ++ ++/* Show login prompt, optionally preceded by /etc/issue contents. */ ++static void do_prompt(struct options *op, struct termios *tp) ++{ ++#ifdef AGETTY_RELOAD ++again: ++#endif ++ print_issue_file(op, tp); ++ + if (op->flags & F_LOGINPAUSE) { + puts(_("[press ENTER to login]")); ++#ifdef AGETTY_RELOAD ++ if (!wait_for_term_input(STDIN_FILENO)) { ++ /* reload issue */ ++ if (op->flags & F_VCONSOLE) ++ termio_clear(STDOUT_FILENO); ++ goto again; ++ } ++#endif + getc(stdin); + } + #ifdef KDGKBLED +@@ -1533,18 +1961,36 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata + *bp = '\0'; + + while (*logname == '\0') { +- + /* Write issue file and prompt */ + do_prompt(op, tp); + ++#ifdef AGETTY_RELOAD ++ if (!wait_for_term_input(STDIN_FILENO)) { ++ /* refresh prompt -- discard input data, clear terminal ++ * and call do_prompt() again ++ */ ++ if ((op->flags & F_VCONSOLE) == 0) ++ sleep(1); ++ tcflush(STDIN_FILENO, TCIFLUSH); ++ if (op->flags & F_VCONSOLE) ++ termio_clear(STDOUT_FILENO); ++ bp = logname; ++ *bp = '\0'; ++ continue; ++ } ++#endif + cp->eol = '\0'; + + /* Read name, watch for break and end-of-line. */ + while (cp->eol == '\0') { + + char key; ++ ssize_t readres; + +- if (read(STDIN_FILENO, &c, 1) < 1) { ++ debug("read from FD\n"); ++ readres = read(STDIN_FILENO, &c, 1); ++ if (readres < 0) { ++ debug("read failed\n"); + + /* The terminal could be open with O_NONBLOCK when + * -L (force CLOCAL) is specified... */ +@@ -1558,12 +2004,15 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata + case ESRCH: + case EINVAL: + case ENOENT: +- break; ++ exit_slowly(EXIT_SUCCESS); + default: + log_err(_("%s: read: %m"), op->tty); + } + } + ++ if (readres == 0) ++ c = 0; ++ + /* Do parity bit handling. */ + if (eightbit) + ascval = c; +@@ -1588,8 +2037,10 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata + switch (key) { + case 0: + *bp = 0; +- if (op->numspeed > 1) ++ if (op->numspeed > 1 && !(op->flags & F_VCONSOLE)) + return NULL; ++ if (readres == 0) ++ exit_slowly(EXIT_SUCCESS); + break; + case CR: + case NL: +@@ -1627,6 +2078,7 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata + } + } + } ++ + #ifdef HAVE_WIDECHAR + if ((op->flags & (F_EIGHTBITS|F_UTF8)) == (F_EIGHTBITS|F_UTF8)) { + /* Check out UTF-8 multibyte characters */ +@@ -1637,7 +2089,7 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata + if (len < 0) + log_err(_("%s: invalid character conversion for login name"), op->tty); + +- wcs = (wchar_t *) malloc((len + 1) * sizeof(wchar_t)); ++ wcs = malloc((len + 1) * sizeof(wchar_t)); + if (!wcs) + log_err(_("failed to allocate memory: %m")); + +@@ -1702,12 +2154,12 @@ static void termio_final(struct options *op, struct termios *tp, struct chardata + case 1: + /* odd parity */ + tp->c_cflag |= PARODD; +- /* do not break */ ++ /* fallthrough */ + case 2: + /* even parity */ + tp->c_cflag |= PARENB; + tp->c_iflag |= INPCK | ISTRIP; +- /* do not break */ ++ /* fallthrough */ + case (1 | 2): + /* no parity bit */ + tp->c_cflag &= ~CSIZE; +@@ -1767,11 +2219,17 @@ static speed_t bcode(char *s) + return 0; + } + +-static void __attribute__ ((__noreturn__)) usage(FILE *out) ++static void __attribute__((__noreturn__)) usage(void) + { ++ FILE *out = stdout; ++ + fputs(USAGE_HEADER, out); +- fprintf(out, _(" %1$s [options] line [baud_rate,...] [termtype]\n" +- " %1$s [options] baud_rate,... line [termtype]\n"), program_invocation_short_name); ++ fprintf(out, _(" %1$s [options] <line> [<baud_rate>,...] [<termtype>]\n" ++ " %1$s [options] <baud_rate>,... <line> [<termtype>]\n"), program_invocation_short_name); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Open a terminal and set its mode.\n"), out); ++ + fputs(USAGE_OPTIONS, out); + fputs(_(" -8, --8bits assume 8-bit tty\n"), out); + fputs(_(" -a, --autologin <user> login the specified user automatically\n"), out); +@@ -1782,10 +2240,12 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out) + fputs(_(" -H, --host <hostname> specify login host\n"), out); + fputs(_(" -i, --noissue do not display issue file\n"), out); + fputs(_(" -I, --init-string <string> set init string\n"), out); ++ fputs(_(" -J --noclear do not clear the screen before prompt\n"), out); + fputs(_(" -l, --login-program <file> specify login program\n"), out); +- fputs(_(" -L, --local-line[=<mode>] cotrol local line flag\n"), out); ++ fputs(_(" -L, --local-line[=<mode>] control the local line flag\n"), out); + fputs(_(" -m, --extract-baud extract baud rate during connect\n"), out); + fputs(_(" -n, --skip-login do not prompt for login\n"), out); ++ fputs(_(" -N --nonewline do not print a newline before issue\n"), out); + fputs(_(" -o, --login-options <opts> options that are passed to login\n"), out); + fputs(_(" -p, --login-pause wait for any key before the login\n"), out); + fputs(_(" -r, --chroot <dir> change root to the directory\n"), out); +@@ -1794,18 +2254,29 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out) + fputs(_(" -t, --timeout <number> login process timeout\n"), out); + fputs(_(" -U, --detect-case detect uppercase terminal\n"), out); + fputs(_(" -w, --wait-cr wait carriage-return\n"), out); +- fputs(_(" --noclear do not clear the screen before prompt\n"), out); + fputs(_(" --nohints do not print hints\n"), out); +- fputs(_(" --nonewline do not print a newline before issue\n"), out); + fputs(_(" --nohostname no hostname at all will be shown\n"), out); + fputs(_(" --long-hostname show full qualified hostname\n"), out); + fputs(_(" --erase-chars <string> additional backspace chars\n"), out); + fputs(_(" --kill-chars <string> additional kill chars\n"), out); +- fputs(_(" --help display this help and exit\n"), out); +- fputs(_(" --version output version information and exit\n"), out); +- fprintf(out, USAGE_MAN_TAIL("agetty(8)")); ++ fputs(_(" --chdir <directory> chdir before the login\n"), out); ++ fputs(_(" --delay <number> sleep seconds before prompt\n"), out); ++ fputs(_(" --nice <number> run login with this priority\n"), out); ++ fputs(_(" --reload reload prompts on running agetty instances\n"), out); ++ fputs(_(" --list-speeds display supported baud rates\n"), out); ++ printf( " --help %s\n", USAGE_OPTSTR_HELP); ++ printf( " --version %s\n", USAGE_OPTSTR_VERSION); ++ printf(USAGE_MAN_TAIL("agetty(8)")); ++ ++ exit(EXIT_SUCCESS); ++} ++ ++static void list_speeds(void) ++{ ++ const struct Speedtab *sp; + +- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); ++ for (sp = speedtab; sp->speed; sp++) ++ printf("%10ld\n", sp->speed); + } + + /* +@@ -1855,6 +2326,13 @@ static void dolog(int priority, const char *fmt, va_list ap) + #endif /* USE_SYSLOG */ + } + ++static void exit_slowly(int code) ++{ ++ /* Be kind to init(8). */ ++ sleep(10); ++ exit(code); ++} ++ + static void log_err(const char *fmt, ...) + { + va_list ap; +@@ -1863,9 +2341,7 @@ static void log_err(const char *fmt, ...) + dolog(LOG_ERR, fmt, ap); + va_end(ap); + +- /* Be kind to init(8). */ +- sleep(10); +- exit(EXIT_FAILURE); ++ exit_slowly(EXIT_FAILURE); + } + + static void log_warn(const char *fmt, ...) +@@ -1877,41 +2353,70 @@ static void log_warn(const char *fmt, ...) + va_end(ap); + } + +-static void output_iface_ip(struct ifaddrs *addrs, const char *iface, sa_family_t family) ++static void print_addr(sa_family_t family, void *addr) ++{ ++ char buff[INET6_ADDRSTRLEN + 1]; ++ ++ inet_ntop(family, addr, buff, sizeof(buff)); ++ printf("%s", buff); ++} ++ ++/* ++ * Prints IP for the specified interface (@iface), if the interface is not ++ * specified then prints the "best" one (UP, RUNNING, non-LOOPBACK). If not ++ * found the "best" interface then prints at least host IP. ++ */ ++static void output_iface_ip(struct ifaddrs *addrs, ++ const char *iface, ++ sa_family_t family) + { +- if (!iface) ++ struct ifaddrs *p; ++ struct addrinfo hints, *info = NULL; ++ char *host = NULL; ++ void *addr = NULL; ++ ++ if (!addrs) + return; + +- if (addrs->ifa_name +- && strcmp(addrs->ifa_name, iface) == 0 +- && addrs->ifa_addr +- && addrs->ifa_addr->sa_family == family) { ++ for (p = addrs; p; p = p->ifa_next) { + +- void *addr = NULL; +- char buff[INET6_ADDRSTRLEN + 1]; ++ if (!p->ifa_name || ++ !p->ifa_addr || ++ p->ifa_addr->sa_family != family) ++ continue; ++ ++ if (iface) { ++ /* Filter out by interface name */ ++ if (strcmp(p->ifa_name, iface) != 0) ++ continue; ++ } else { ++ /* Select the "best" interface */ ++ if ((p->ifa_flags & IFF_LOOPBACK) || ++ !(p->ifa_flags & IFF_UP) || ++ !(p->ifa_flags & IFF_RUNNING)) ++ continue; ++ } + +- switch (addrs->ifa_addr->sa_family) { ++ addr = NULL; ++ switch (p->ifa_addr->sa_family) { + case AF_INET: +- addr = &((struct sockaddr_in *) addrs->ifa_addr)->sin_addr; ++ addr = &((struct sockaddr_in *) p->ifa_addr)->sin_addr; + break; + case AF_INET6: +- addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr; ++ addr = &((struct sockaddr_in6 *) p->ifa_addr)->sin6_addr; + break; + } ++ + if (addr) { +- inet_ntop(addrs->ifa_addr->sa_family, addr, buff, sizeof(buff)); +- printf("%s", buff); ++ print_addr(family, addr); ++ return; + } ++ } + +- } else if (addrs->ifa_next) +- output_iface_ip(addrs->ifa_next, iface, family); +-} +- +-static void output_ip(sa_family_t family) +-{ +- char *host; +- struct addrinfo hints, *info = NULL; ++ if (iface) ++ return; + ++ /* Hmm.. not found the best interface, print host IP at least */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + if (family == AF_INET6) +@@ -1919,9 +2424,6 @@ static void output_ip(sa_family_t family) + + host = xgethostname(); + if (host && getaddrinfo(host, NULL, &hints, &info) == 0 && info) { +- +- void *addr = NULL; +- + switch (info->ai_family) { + case AF_INET: + addr = &((struct sockaddr_in *) info->ai_addr)->sin_addr; +@@ -1930,12 +2432,8 @@ static void output_ip(sa_family_t family) + addr = &((struct sockaddr_in6 *) info->ai_addr)->sin6_addr; + break; + } +- if (addr) { +- char buff[INET6_ADDRSTRLEN + 1]; +- +- inet_ntop(info->ai_family, (void *) addr, buff, sizeof(buff)); +- printf("%s", buff); +- } ++ if (addr) ++ print_addr(family, addr); + + freeaddrinfo(info); + } +@@ -1974,22 +2472,25 @@ static void output_special_char(unsigned char c, struct options *op, + { + struct utsname uts; + +- uname(&uts); +- + switch (c) { + case 's': ++ uname(&uts); + printf("%s", uts.sysname); + break; + case 'n': ++ uname(&uts); + printf("%s", uts.nodename); + break; + case 'r': ++ uname(&uts); + printf("%s", uts.release); + break; + case 'v': ++ uname(&uts); + printf("%s", uts.version); + break; + case 'm': ++ uname(&uts); + printf("%s", uts.machine); + break; + case 'o': +@@ -2066,30 +2567,39 @@ static void output_special_char(unsigned char c, struct options *op, + { + char *var = NULL, varname[64]; + +- if (get_escape_argument(fp, varname, sizeof(varname))) ++ /* \S{varname} */ ++ if (get_escape_argument(fp, varname, sizeof(varname))) { + var = read_os_release(op, varname); +- else if (!(var = read_os_release(op, "PRETTY_NAME"))) +- var = uts.sysname; +- if (var) { +- if (strcmp(varname, "ANSI_COLOR") == 0) +- printf("\033[%sm", var); +- else +- printf("%s", var); +- if (var != uts.sysname) +- free(var); ++ if (var) { ++ if (strcmp(varname, "ANSI_COLOR") == 0) ++ printf("\033[%sm", var); ++ else ++ fputs(var, stdout); ++ } ++ /* \S */ ++ } else if ((var = read_os_release(op, "PRETTY_NAME"))) { ++ fputs(var, stdout); ++ ++ /* \S and PRETTY_NAME not found */ ++ } else { ++ uname(&uts); ++ fputs(uts.sysname, stdout); + } ++ ++ free(var); ++ + break; + } + case 'u': + case 'U': + { + int users = 0; +- struct utmp *ut; +- setutent(); +- while ((ut = getutent())) ++ struct utmpx *ut; ++ setutxent(); ++ while ((ut = getutxent())) + if (ut->ut_type == USER_PROCESS) + users++; +- endutent(); ++ endutxent(); + if (c == 'U') + printf(P_("%d user", "%d users", users), users); + else +@@ -2100,17 +2610,22 @@ static void output_special_char(unsigned char c, struct options *op, + case '6': + { + sa_family_t family = c == '4' ? AF_INET : AF_INET6; ++ struct ifaddrs *addrs = NULL; + char iface[128]; + +- if (get_escape_argument(fp, iface, sizeof(iface))) { /* interface IP */ +- struct ifaddrs *addrs; +- int status = getifaddrs(&addrs); +- if (status != 0) +- break; ++#ifdef AGETTY_RELOAD ++ open_netlink(); ++#endif ++ ++ if (getifaddrs(&addrs)) ++ break; ++ ++ if (get_escape_argument(fp, iface, sizeof(iface))) + output_iface_ip(addrs, iface, family); +- freeifaddrs(addrs); +- } else /* host IP */ +- output_ip(family); ++ else ++ output_iface_ip(addrs, NULL, family); ++ ++ freeifaddrs(addrs); + break; + } + default: +@@ -2162,7 +2677,7 @@ static void init_special_char(char* arg, struct options *op) + } + + /* +- * Appends @str to @dest and if @dest is not empty then use use @sep as a ++ * Appends @str to @dest and if @dest is not empty then use @sep as a + * separator. The maximal final length of the @dest is @len. + * + * Returns the final @dest length or -1 in case of error. +@@ -2217,3 +2732,19 @@ err: + log_err(_("checkname failed: %m")); + } + ++static void reload_agettys(void) ++{ ++#ifdef AGETTY_RELOAD ++ int fd = open(AGETTY_RELOAD_FILENAME, O_CREAT|O_CLOEXEC|O_WRONLY, ++ S_IRUSR|S_IWUSR); ++ if (fd < 0) ++ err(EXIT_FAILURE, _("cannot open %s"), AGETTY_RELOAD_FILENAME); ++ ++ if (futimens(fd, NULL) < 0 || close(fd) < 0) ++ err(EXIT_FAILURE, _("cannot touch file %s"), ++ AGETTY_RELOAD_FILENAME); ++#else ++ /* very unusual */ ++ errx(EXIT_FAILURE, _("--reload is unsupported on your system")); ++#endif ++} +-- +2.20.1 + diff --git a/SOURCES/0180-swapon-mkswap-sync-man-page-with-upstream.patch b/SOURCES/0180-swapon-mkswap-sync-man-page-with-upstream.patch new file mode 100644 index 0000000..ba7f70b --- /dev/null +++ b/SOURCES/0180-swapon-mkswap-sync-man-page-with-upstream.patch @@ -0,0 +1,417 @@ +From ec6f608028249480bae04c062089cb9120fcd8bd Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 21 Mar 2019 13:21:54 +0100 +Subject: [PATCH 180/185] swapon, mkswap: sync man page with upstream + +Clean up all notes about swap-area restrictions (fallocate, XFS, +BTRFS, NFS, ...) + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1203378 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + disk-utils/mkswap.8 | 42 +++++----- + sys-utils/swapon.8 | 186 ++++++++++++++++++++------------------------ + 2 files changed, 104 insertions(+), 124 deletions(-) + +diff --git a/disk-utils/mkswap.8 b/disk-utils/mkswap.8 +index 6435dd6ae..b57433388 100644 +--- a/disk-utils/mkswap.8 ++++ b/disk-utils/mkswap.8 +@@ -1,14 +1,13 @@ + .\" Copyright 1998 Andries E. Brouwer (aeb@cwi.nl) + .\" + .\" May be distributed under the GNU General Public License +-.\" Rewritten for 2.1.117, aeb, 981010. + .\" + .TH MKSWAP 8 "March 2009" "util-linux" "System Administration" + .SH NAME + mkswap \- set up a Linux swap area + .SH SYNOPSIS + .B mkswap +-.RI [ options ] ++[options] + .I device + .RI [ size ] + .SH DESCRIPTION +@@ -32,14 +31,14 @@ parameter is superfluous but retained for backwards compatibility. + (It specifies the desired size of the swap area in 1024-byte blocks. + .B mkswap + will use the entire partition or file if it is omitted. +-Specifying it is unwise -- a typo may destroy your disk.) ++Specifying it is unwise \(en a typo may destroy your disk.) + + After creating the swap area, you need the + .B swapon + command to start using it. Usually swap areas are listed in + .I /etc/fstab + so that they can be taken into use at boot time by a +-.B swapon -a ++.B swapon \-a + command in some boot script. + + .SH WARNING +@@ -54,7 +53,7 @@ like many others mkfs-like utils, + However, + .B mkswap + refuses to erase the first block on a device with a disk +-label (SUN, BSD, ...). ++label (SUN, BSD, \&...\&). + + .SH OPTIONS + .TP +@@ -103,48 +102,47 @@ Display version information and exit. + .SH NOTES + The maximum useful size of a swap area depends on the architecture and + the kernel version. +-It is roughly 2GiB on i386, PPC, m68k and ARM, 1GiB on sparc, 512MiB on mips, +-128GiB on alpha, and 3TiB on sparc64. For kernels after 2.3.3 (May 1999) there is no +-such limitation. + +-Note that before version 2.1.117 the kernel allocated one byte for each page, +-while it now allocates two bytes, so that taking into use a swap area of 2 GiB +-might require 2 MiB of kernel memory. ++The maximum number of the pages that is possible to address by swap area header ++is 4294967295 (32-bit unsigned int). The remaining space on the swap device is ignored. + +-Presently, Linux allows 32 swap areas (this was 8 before Linux 2.4.10 (Sep 2001)). ++Presently, Linux allows 32 swap areas. + The areas in use can be seen in the file + .I /proc/swaps +-(since 2.1.25 (Sep 1997)). + + .B mkswap + refuses areas smaller than 10 pages. + + If you don't know the page size that your machine uses, you may be +-able to look it up with "cat /proc/cpuinfo" (or you may not -- ++able to look it up with "cat /proc/cpuinfo" (or you may not \(en + the contents of this file depend on architecture and kernel version). + + To set up a swap file, it is necessary to create that file before + initializing it with + .BR mkswap , +-e.g. using a command like ++e.g.\& using a command like + + .nf + .RS +-# fallocate --length 8GiB swapfile ++# dd if=/dev/zero of=swapfile bs=1MiB count=$((8*1024)) + .RE + .fi + +-Note that a swap file must not contain any holes (so, using +-.BR cp (1) +-to create the file is not acceptable). ++to create 8GiB swapfile. ++ ++Please read notes from ++.BR swapon (8) ++about ++.B the swap file use restrictions ++(holes, preallocation and copy-on-write issues). + + .SH ENVIRONMENT +-.IP LIBBLKID_DEBUG=0xffff +-enables debug output. ++.IP LIBBLKID_DEBUG=all ++enables libblkid debug output. + + .SH "SEE ALSO" + .BR fdisk (8), + .BR swapon (8) + .SH AVAILABILITY + The mkswap command is part of the util-linux package and is available from +-ftp://ftp.kernel.org/pub/linux/utils/util-linux/. ++https://www.kernel.org/pub/linux/utils/util-linux/. +diff --git a/sys-utils/swapon.8 b/sys-utils/swapon.8 +index 836b17277..a45b38865 100644 +--- a/sys-utils/swapon.8 ++++ b/sys-utils/swapon.8 +@@ -31,59 +31,24 @@ + .\" + .\" @(#)swapon.8 6.3 (Berkeley) 3/16/91 + .\" +-.\" Sun Dec 27 12:31:30 1992: Modified by faith@cs.unc.edu +-.\" Sat Mar 6 20:46:02 1993: Modified by faith@cs.unc.edu +-.\" Sat Oct 9 09:35:30 1993: Converted to man format by faith@cs.unc.edu +-.\" Sat Nov 27 20:22:42 1993: Updated authorship information, faith@cs.unc.edu +-.\" Mon Sep 25 14:12:38 1995: Added -v and -p information +-.\" Tue Apr 30 03:32:07 1996: Added some text from A. Koppenhoefer +-.\" +-.TH SWAPON 8 "September 1995" "util-linux" "System Administration" ++.TH SWAPON 8 "October 2014" "util-linux" "System Administration" + .SH NAME + swapon, swapoff \- enable/disable devices and files for paging and swapping + .SH SYNOPSIS +-Get info: +-.br +-.in +5 +-.B swapon \-s +-.RB [ \-h ] +-.RB [ \-V ] +-.sp +-.in -5 +-Enable/disable: +-.br +-.in +5 + .B swapon +-.RB [ \-d ] +-.RB [ \-f ] +-.RB [ \-p +-.IR priority ] +-.RB [ \-v ] +-.IR specialfile ... ++[options] ++.RI [ specialfile ...] + .br + .B swapoff +-.RB [ \-v ] +-.IR specialfile ... +-.sp +-.in -5 +-Enable/disable all: +-.br +-.in +5 +-.B swapon \-a +-.RB [ \-e ] +-.RB [ \-f ] +-.RB [ \-v ] +-.br +-.B swapoff \-a +-.RB [ \-v ] +-.in -5 ++.RB [ \-va ] ++.RI [ specialfile ...] + .SH DESCRIPTION + .B swapon + is used to specify devices on which paging and swapping are to take place. + + The device or file used is given by the + .I specialfile +-parameter. It may be of the form ++parameter. It may be of the form + .BI \-L " label" + or + .BI \-U " uuid" +@@ -105,82 +70,85 @@ flag is given, swapping is disabled on all known swap devices and files + or + .IR /etc/fstab ). + ++.SH OPTIONS + .TP +-.B "\-a, \-\-all" ++.BR \-a , " \-\-all" + All devices marked as ``swap'' in + .I /etc/fstab + 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\fR [=\fIpolicy\fR]" ++.BR \-d , " \-\-discard" [ =\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 ++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 ++.B \-\-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. ++.B \-\-discard=pages ++to asynchronously discard freed swap pages before they are available for reuse. + If no policy is selected, the default behavior is to enable both discard types. + The + .I /etc/fstab + mount options +-.BI discard, +-.BI discard=once, ++.BR discard , ++.BR discard=once , + or +-.BI discard=pages +-may be also used to enable discard flags. ++.B discard=pages ++may also be used to enable discard flags. + .TP +-.B "\-e, \-\-ifexists" ++.BR \-e , " \-\-ifexists" + Silently skip devices that do not exist. + The + .I /etc/fstab + mount option +-.BI nofail +-may be also used to skip non-existing device. ++.B nofail ++may also be used to skip non-existing device. + + .TP +-.B "\-f, \-\-fixpgsz" +-Reinitialize (exec /sbin/mkswap) the swap space if its page size does not ++.BR \-f , " \-\-fixpgsz" ++Reinitialize (exec mkswap) the swap space if its page size does not + match that of the current running kernel. + .BR mkswap (2) + initializes the whole device and does not check for bad blocks. + .TP +-.B \-h, \-\-help +-Provide help. ++.BR \-h , " \-\-help" ++Display help text and exit. + .TP +-.B "\-L \fIlabel\fP" ++.BI \-L " label" + Use the partition that has the specified + .IR label . + (For this, access to + .I /proc/partitions + is needed.) + .TP +-.B "\-p, \-\-priority \fIpriority\fP" ++.BR \-p , " \-\-priority " \fIpriority\fP + Specify the priority of the swap device. + .I priority + is a value between \-1 and 32767. Higher numbers indicate + higher priority. See + .BR swapon (2) +-for a full description of swap priorities. Add ++for a full description of swap priorities. Add + .BI pri= value + to the option field of + .I /etc/fstab + for use with + .BR "swapon -a" . +-When priority is not defined it defaults to \-1. ++When no priority is defined, it defaults to \-1. + .TP +-.B "\-s, \-\-summary" +-Display swap usage summary by device. Equivalent to "cat /proc/swaps". +-Not available before Linux 2.1.25. ++.BR \-s , " \-\-summary" ++Display swap usage summary by device. Equivalent to "cat /proc/swaps". ++This output format is DEPRECATED in favour ++of \fB\-\-show\fR that provides better control on output data. + .TP +-\fB\-\-show\fR [\fIcolumn,column\fR] +-Display definable device table similar to +-.B \-\-summary +-output. See \-\-help output for +-.I column +-list. ++.BR \-\-show [ =\fIcolumn\fR ...] ++Display a definable table of swap areas. See the ++.B \-\-help ++output for a list of available columns. ++.TP ++.B \-\-output\-all ++Output all available columns. + .TP + .B \-\-noheadings + Do not print headings when displaying +@@ -195,51 +163,65 @@ output without aligning table columns. + .B \-\-bytes + Display swap size in bytes in + .B \-\-show +-output instead of user friendly size and unit. +-.B "\-U \fIuuid\fP" ++output instead of in user-friendly units. ++.TP ++.BI \-U " uuid" + Use the partition that has the specified + .IR uuid . + .TP +-.B "\-v, \-\-verbose" ++.BR \-v , " \-\-verbose" + Be verbose. + .TP +-.B "\-V, \-\-version" +-Display version. ++.BR \-V , " \-\-version" ++Display version information and exit. + .SH NOTES +-You should not use +-.B swapon +-on a file with holes. +-Swap over NFS may not work. +-.PP ++.SS Files with holes ++The swap file implementation in the kernel expects to be able to write to the ++file directly, without the assistance of the filesystem. This is a problem on ++files with holes or on copy-on-write files on filesystems like Btrfs. ++.sp ++Commands like ++.BR cp (1) ++or ++.BR truncate (1) ++create files with holes. These files will be rejected by swapon. ++.sp ++Preallocated files created by ++.BR fallocate (1) ++may be interpreted as files with holes too depending of the filesystem. ++Preallocated swap files are supported on XFS since Linux 4.18. ++.sp ++The most portable solution to create a swap file is to use ++.BR dd (1) ++and /dev/zero. ++.SS Btrfs ++Swap files on Btrfs are supported since Linux 5.0 on files with nocow attribute. ++See the ++.BR btrfs (5) ++manual page for more details. ++.SS NFS ++Swap over \fBNFS\fR may not work. ++.SS Suspend + .B swapon +-automatically detects and rewrites swap space signature with old software +-suspend data (e.g S1SUSPEND, S2SUSPEND, ...). The problem is that if we don't ++automatically detects and rewrites a swap space signature with old software ++suspend data (e.g. S1SUSPEND, S2SUSPEND, ...). The problem is that if we don't + do it, then we get data corruption the next time an attempt at unsuspending is + made. +-.PP +-.B swapon +-may not work correctly when using a swap file with some versions of btrfs. +-This is due to the swap file implementation in the kernel expecting to be able +-to write to the file directly, without the assistance of the file system. +-Since btrfs is a copy-on-write file system, the file location may not be +-static and corruption can result. Btrfs actively disallows the use of files +-on its file systems by refusing to map the file. This can be seen in the system +-log as "swapon: swapfile has holes." One possible workaround is to map the +-file to a loopback device. This will allow the file system to determine the +-mapping properly but may come with a performance impact. +- + .SH ENVIRONMENT +-.IP LIBMOUNT_DEBUG=0xffff +-enables debug output. ++.IP LIBMOUNT_DEBUG=all ++enables libmount debug output. ++.IP LIBBLKID_DEBUG=all ++enables libblkid debug output. + + .SH SEE ALSO +-.BR swapon (2), + .BR swapoff (2), ++.BR swapon (2), + .BR fstab (5), + .BR init (8), ++.BR fallocate (1), + .BR mkswap (8), +-.BR rc (8), +-.BR mount (8) ++.BR mount (8), ++.BR rc (8) + .SH FILES + .br + .I /dev/sd?? +@@ -253,4 +235,4 @@ The + command appeared in 4.0BSD. + .SH AVAILABILITY + The swapon command is part of the util-linux package and is available from +-ftp://ftp.kernel.org/pub/linux/utils/util-linux/. ++https://www.kernel.org/pub/linux/utils/util-linux/. +-- +2.20.1 + diff --git a/SOURCES/0181-libblkid-zeroize-errno-on-blkid_probe_get_buffer-suc.patch b/SOURCES/0181-libblkid-zeroize-errno-on-blkid_probe_get_buffer-suc.patch new file mode 100644 index 0000000..80fdf45 --- /dev/null +++ b/SOURCES/0181-libblkid-zeroize-errno-on-blkid_probe_get_buffer-suc.patch @@ -0,0 +1,46 @@ +From bb147e4f4008b5debde0f87282967a5e52d2d50f Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 7 Oct 2014 12:44:34 +0200 +Subject: [PATCH 181/185] libblkid: zeroize errno on blkid_probe_get_buffer() + success + +Since 37f4060225df0591ab8e1dd676dbc8115d900d4f prober functions are +sensitive to errno, it seems more robust to set errno=0 with in +blkid_probe_get_buffer() on success than set the zero on all places +where we call blkid_probe_get_buffer(). + +Addresses: https://github.com/karelzak/util-linux/issues/119 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1632944 +Upstream: http://github.com/karelzak/util-linux/commit/00749bc366fe6106df918660a92a388cfff4f35f +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/probe.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/libblkid/src/probe.c b/libblkid/src/probe.c +index 07b08441f..ef0a72299 100644 +--- a/libblkid/src/probe.c ++++ b/libblkid/src/probe.c +@@ -529,8 +529,10 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, + struct list_head *p; + struct blkid_bufinfo *bf = NULL; + +- if (pr->size <= 0) ++ if (pr->size <= 0) { ++ errno = EINVAL; + return NULL; ++ } + + if (pr->parent && + pr->parent->devno == pr->devno && +@@ -598,6 +600,7 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, + list_add_tail(&bf->bufs, &pr->buffers); + } + ++ errno = 0; + return off ? bf->data + (off - bf->off) : bf->data; + } + +-- +2.20.1 + diff --git a/SOURCES/0182-libmount-fix-mount-a-for-cifs.patch b/SOURCES/0182-libmount-fix-mount-a-for-cifs.patch new file mode 100644 index 0000000..874b4f7 --- /dev/null +++ b/SOURCES/0182-libmount-fix-mount-a-for-cifs.patch @@ -0,0 +1,147 @@ +From 597807396a00e6f31aa03805ddca27875b81440b Mon Sep 17 00:00:00 2001 +From: Aurelien Aptel <aaptel@suse.com> +Date: Thu, 1 Sep 2016 13:57:42 +0200 +Subject: [PATCH 182/185] libmount: fix mount -a for cifs + +when mounting a cifs share, the src is actually an UNC path which can in +in several forms: + +simple: //host/share, //host/share/ +including subpath: //host/share/sub/path + +to check if the cifs fs is mounted we have to extract the subpath and +compare *that* to the root. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1633657 +Upstream: http://github.com/karelzak/util-linux/commit/76d4fba2e47808264f5c4c883127b0d275e31949 +Signed-off-by: Aurelien Aptel <aaptel@suse.com> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + include/strutils.h | 1 + + lib/strutils.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++ + libmount/src/tab.c | 29 ++++++++++++++++++++++++--- + 3 files changed, 77 insertions(+), 3 deletions(-) + +diff --git a/include/strutils.h b/include/strutils.h +index 822fb7d49..83fdc7ead 100644 +--- a/include/strutils.h ++++ b/include/strutils.h +@@ -108,6 +108,7 @@ extern int string_to_bitmask(const char *list, + extern int parse_range(const char *str, int *lower, int *upper, int def); + + extern int streq_except_trailing_slash(const char *s1, const char *s2); ++extern int streq_paths(const char *a, const char *b); + + extern char *strnappend(const char *s, const char *suffix, size_t b); + extern char *strappend(const char *s, const char *suffix); +diff --git a/lib/strutils.c b/lib/strutils.c +index 2458a2c2f..2f264daeb 100644 +--- a/lib/strutils.c ++++ b/lib/strutils.c +@@ -697,6 +697,56 @@ int streq_except_trailing_slash(const char *s1, const char *s2) + return equal; + } + ++static const char *next_path_segment(const char *str, size_t *sz) ++{ ++ const char *start, *p; ++ ++ start = str; ++ *sz = 0; ++ while (start && *start == '/' && *(start + 1) == '/') ++ start++; ++ ++ if (!start || !*start) ++ return NULL; ++ ++ for (*sz = 1, p = start + 1; *p && *p != '/'; p++) { ++ (*sz)++; ++ } ++ ++ return start; ++} ++ ++int streq_paths(const char *a, const char *b) ++{ ++ while (a && b) { ++ size_t a_sz, b_sz; ++ const char *a_seg = next_path_segment(a, &a_sz); ++ const char *b_seg = next_path_segment(b, &b_sz); ++ ++ /* ++ fprintf(stderr, "A>>>(%zu) '%s'\n", a_sz, a_seg); ++ fprintf(stderr, "B>>>(%zu) '%s'\n", b_sz, b_seg); ++ */ ++ ++ /* end of the path */ ++ if (a_sz + b_sz == 0) ++ return 1; ++ ++ /* ignore tailing slash */ ++ if (a_sz + b_sz == 1 && ++ ((a_seg && *a_seg == '/') || (b_seg && *b_seg == '/'))) ++ return 1; ++ ++ if (a_sz != b_sz || strncmp(a_seg, b_seg, a_sz) != 0) ++ return 0; ++ ++ a = a_seg + a_sz; ++ b = b_seg + b_sz; ++ }; ++ ++ return 0; ++} ++ + char *strnappend(const char *s, const char *suffix, size_t b) + { + size_t a; +diff --git a/libmount/src/tab.c b/libmount/src/tab.c +index dfa1da822..3ee9c0042 100644 +--- a/libmount/src/tab.c ++++ b/libmount/src/tab.c +@@ -1053,6 +1053,22 @@ static int is_mountinfo(struct libmnt_table *tb) + return 0; + } + ++ ++ ++static const char *get_cifs_unc_subdir_path (const char *unc) ++{ ++ /* ++ * 1 or more slash: %*[/] ++ * 1 or more non-slash: %*[^/] ++ * number of byte read: %n ++ */ ++ int share_end = 0; ++ int r = sscanf(unc, "%*[/]%*[^/]%*[/]%*[^/]%n", &share_end); ++ if (r == EOF || share_end == 0) ++ return NULL; ++ return unc + share_end; ++} ++ + /** + * mnt_table_is_mounted: + * @tb: /proc/self/mountinfo file +@@ -1150,9 +1166,16 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) + } + + if (root) { +- const char *r = mnt_fs_get_root(fs); +- if (!r || strcmp(r, root) != 0) +- continue; ++ if (strcmp(mnt_fs_get_fstype(fs), "cifs") == 0) { ++ const char *unc_subdir = get_cifs_unc_subdir_path(src); ++ const char *path_on_fs = mnt_fs_get_root(fs); ++ if (!unc_subdir || !path_on_fs || !streq_paths(unc_subdir, path_on_fs)) ++ continue; ++ } else { ++ const char *r = mnt_fs_get_root(fs); ++ if (!r || strcmp(r, root) != 0) ++ continue; ++ } + } + + /* +-- +2.20.1 + diff --git a/SOURCES/0183-agetty-fix-man-page-formatting.patch b/SOURCES/0183-agetty-fix-man-page-formatting.patch new file mode 100644 index 0000000..631f662 --- /dev/null +++ b/SOURCES/0183-agetty-fix-man-page-formatting.patch @@ -0,0 +1,27 @@ +From f4adc45048588d50115c3fad9e1ae1dee8ba205c Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 21 Mar 2019 13:45:41 +0100 +Subject: [PATCH 183/185] agetty: fix man page formatting + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1639465 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + term-utils/agetty.8 | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/term-utils/agetty.8 b/term-utils/agetty.8 +index fe4bfd427..5ef3815e9 100644 +--- a/term-utils/agetty.8 ++++ b/term-utils/agetty.8 +@@ -350,7 +350,7 @@ Insert the number of current users logged in. + U + Insert the string "1 user" or "<n> users" where <n> is the number of current + users logged in. +-.PP ++.TP + v + Insert the version of the OS, eg. the build-date etc. + .PP +-- +2.20.1 + diff --git a/SOURCES/0184-build-sys-add-missing-open_memstream-test.patch b/SOURCES/0184-build-sys-add-missing-open_memstream-test.patch new file mode 100644 index 0000000..fc4edcd --- /dev/null +++ b/SOURCES/0184-build-sys-add-missing-open_memstream-test.patch @@ -0,0 +1,26 @@ +From 43e53c69be2337b75e01919b14681e207ee05251 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Thu, 21 Mar 2019 13:54:19 +0100 +Subject: [PATCH 184/185] build-sys: add missing open_memstream() test + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1667942 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + configure.ac | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/configure.ac b/configure.ac +index 3f07df495..7381a9e5e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -358,6 +358,7 @@ AC_FUNC_FSEEKO + + AC_CHECK_FUNCS([openat fstatat unlinkat], [have_openat=yes], [have_openat=no]) + AC_CHECK_FUNCS([ioperm iopl], [have_io=yes]) ++AC_CHECK_FUNCS([open_memstream], [have_open_memstream=yes],[have_open_memstream=no]) + + AC_CHECK_MEMBER(struct sockaddr.sa_len, + AC_DEFINE_UNQUOTED(HAVE_SA_LEN,1,[Define if struct sockaddr contains sa_len]),, +-- +2.20.1 + diff --git a/SOURCES/0185-lib-canonicalize-make-DM-canonicalization-more-robus.patch b/SOURCES/0185-lib-canonicalize-make-DM-canonicalization-more-robus.patch new file mode 100644 index 0000000..7e59026 --- /dev/null +++ b/SOURCES/0185-lib-canonicalize-make-DM-canonicalization-more-robus.patch @@ -0,0 +1,97 @@ +From 36e285eb912c86b607b752964c85aa6ee4bc25ea Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 20 May 2014 10:11:57 +0200 +Subject: [PATCH 185/185] lib/canonicalize: make DM canonicalization more + robust + +The current code cares about filenames, but it's too fragile, we have +to check the path is really path to the block device. + +Addresses: https://github.com/karelzak/util-linux/issues/83 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1678451 +Upstream: http://github.com/karelzak/util-linux/commit/1dbbd85b9498536ab38758a03c9ec20362f1c572 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + lib/canonicalize.c | 35 ++++++++++++++++++++++++++--------- + 1 file changed, 26 insertions(+), 9 deletions(-) + +diff --git a/lib/canonicalize.c b/lib/canonicalize.c +index 548e29b75..814ee3dd8 100644 +--- a/lib/canonicalize.c ++++ b/lib/canonicalize.c +@@ -26,6 +26,8 @@ + #include <unistd.h> + #include <errno.h> + #include <stdlib.h> ++#include <sys/types.h> ++#include <sys/stat.h> + + #include "canonicalize.h" + +@@ -170,10 +172,28 @@ canonicalize_dm_name(const char *ptname) + return res; + } + ++static int is_dm_devname(char *canonical, char **name) ++{ ++ struct stat sb; ++ char *p = strrchr(canonical, '/'); ++ ++ *name = NULL; ++ ++ if (!p ++ || strncmp(p, "/dm-", 4) != 0 ++ || !isdigit(*(p + 4)) ++ || stat(canonical, &sb) != 0 ++ || !S_ISBLK(sb.st_mode)) ++ return 0; ++ ++ *name = p + 1; ++ return 1; ++} ++ + char * + canonicalize_path(const char *path) + { +- char canonical[PATH_MAX+2]; ++ char canonical[PATH_MAX+2], *dmname; + char *p; + + if (!path || !*path) +@@ -182,10 +202,8 @@ canonicalize_path(const char *path) + if (!myrealpath(path, canonical, PATH_MAX+1)) + return strdup(path); + +- +- p = strrchr(canonical, '/'); +- if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) { +- p = canonicalize_dm_name(p+1); ++ if (is_dm_devname(canonical, &dmname)) { ++ p = canonicalize_dm_name(dmname); + if (p) + return p; + } +@@ -196,7 +214,7 @@ canonicalize_path(const char *path) + char * + canonicalize_path_restricted(const char *path) + { +- char canonical[PATH_MAX+2]; ++ char canonical[PATH_MAX+2], *dmname; + char *p = NULL; + int errsv; + uid_t euid; +@@ -215,9 +233,8 @@ canonicalize_path_restricted(const char *path) + errsv = errno = 0; + + if (myrealpath(path, canonical, PATH_MAX+1)) { +- p = strrchr(canonical, '/'); +- if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) +- p = canonicalize_dm_name(p+1); ++ if (is_dm_devname(canonical, &dmname)) ++ p = canonicalize_dm_name(dmname); + else + p = NULL; + if (!p) +-- +2.20.1 + diff --git a/SOURCES/0186-libblkid-fix-file-descriptor-leak-in-blkid_verify.patch b/SOURCES/0186-libblkid-fix-file-descriptor-leak-in-blkid_verify.patch new file mode 100644 index 0000000..c4161e7 --- /dev/null +++ b/SOURCES/0186-libblkid-fix-file-descriptor-leak-in-blkid_verify.patch @@ -0,0 +1,87 @@ +From b99078ca40eb4fe9aca6f9bfbd5939cdbebd2aa4 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 31 Jul 2019 16:18:27 +0200 +Subject: [PATCH 186/189] libblkid: fix file descriptor leak in blkid_verify() + +The function blkid_verify() uses private device file descriptor and +uses blkid_probe_set_device() to assign the descriptor to low-level +probing code. Unfortunately, close() in this case is not enough as the +prober can internally open whole-disk device too. + +The library API has been extended so blkid_probe_set_device() +deallocates and close previously used prober for whole-disk. This new +functionality is used in blkid_verify() now. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1734545 +Upstream: http://github.com/karelzak/util-linux/commit/c4d6d1c54dcd0eff701236d396d88b1fc6251768 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/probe.c | 19 ++++++++++++++++--- + libblkid/src/verify.c | 4 +++- + 2 files changed, 19 insertions(+), 4 deletions(-) + +diff --git a/libblkid/src/probe.c b/libblkid/src/probe.c +index ef0a72299..826ffa1c7 100644 +--- a/libblkid/src/probe.c ++++ b/libblkid/src/probe.c +@@ -653,10 +653,15 @@ int blkid_probe_is_cdrom(blkid_probe pr) + * @off: begin of probing area + * @size: size of probing area (zero means whole device/file) + * +- * Assigns the device to probe control struct, resets internal buffers and +- * resets the current probing. ++ * Assigns the device to probe control struct, resets internal buffers, resets ++ * the current probing, and close previously associated device (if open by ++ * libblkid). + * +- * Returns: -1 in case of failure, or 0 on success. ++ * If @fd is < 0 than only resets the prober and returns 1. Note that ++ * blkid_reset_probe() keeps the device associated with the prober, but ++ * blkid_probe_set_device() does complete reset. ++ * ++ * Returns: -1 in case of failure, 0 on success and 1 on reset. + */ + int blkid_probe_set_device(blkid_probe pr, int fd, + blkid_loff_t off, blkid_loff_t size) +@@ -672,6 +677,11 @@ int blkid_probe_set_device(blkid_probe pr, int fd, + if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0) + close(pr->fd); + ++ if (pr->disk_probe) { ++ blkid_free_probe(pr->disk_probe); ++ pr->disk_probe = NULL; ++ } ++ + pr->flags &= ~BLKID_FL_PRIVATE_FD; + pr->flags &= ~BLKID_FL_TINY_DEV; + pr->flags &= ~BLKID_FL_CDROM_DEV; +@@ -687,6 +697,9 @@ int blkid_probe_set_device(blkid_probe pr, int fd, + pr->wipe_size = 0; + pr->wipe_chain = NULL; + ++ if (fd < 0) ++ return 1; ++ + #if defined(POSIX_FADV_RANDOM) && defined(HAVE_POSIX_FADVISE) + /* Disable read-ahead */ + posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM); +diff --git a/libblkid/src/verify.c b/libblkid/src/verify.c +index 2d64d97ca..d5c3592b6 100644 +--- a/libblkid/src/verify.c ++++ b/libblkid/src/verify.c +@@ -182,9 +182,11 @@ blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev) + dev->bid_name, (long long)st.st_rdev, dev->bid_type)); + } + +- blkid_reset_probe(cache->probe); ++ /* reset prober */ + blkid_probe_reset_superblocks_filter(cache->probe); ++ blkid_probe_set_device(cache->probe, -1, 0, 0); + close(fd); ++ + return dev; + } + +-- +2.21.0 + diff --git a/SOURCES/0187-misc-cleanup-size_t-vs.-int-for-string_add_to_idarra.patch b/SOURCES/0187-misc-cleanup-size_t-vs.-int-for-string_add_to_idarra.patch new file mode 100644 index 0000000..0e0b66b --- /dev/null +++ b/SOURCES/0187-misc-cleanup-size_t-vs.-int-for-string_add_to_idarra.patch @@ -0,0 +1,321 @@ +From 1940ca0dff25d02aee10a96282d6166fe4b03331 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 6 Aug 2019 11:19:13 +0200 +Subject: [PATCH 187/189] misc: cleanup size_t vs. int for + string_add_to_idarray() + +The newly backported utils use size_t, but the original code is still +based on int to count output columns. Let's use int everywhere.. + +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1712768 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + login-utils/lslogins.c | 8 ++++---- + sys-utils/lsipc.c | 20 ++++++++++---------- + sys-utils/lsmem.c | 11 +++++------ + sys-utils/lsns.c | 10 +++++----- + sys-utils/wdctl.c | 7 ++++--- + sys-utils/zramctl.c | 8 ++++---- + 6 files changed, 32 insertions(+), 32 deletions(-) + +diff --git a/login-utils/lslogins.c b/login-utils/lslogins.c +index 041053625..33d4777e5 100644 +--- a/login-utils/lslogins.c ++++ b/login-utils/lslogins.c +@@ -438,8 +438,8 @@ static struct utmp *get_last_wtmp(struct lslogins_control *ctl, const char *user + + static int require_wtmp(void) + { +- size_t i; +- for (i = 0; i < (size_t) ncolumns; i++) ++ int i; ++ for (i = 0; i < ncolumns; i++) + if (is_wtmp_col(columns[i])) + return 1; + return 0; +@@ -447,8 +447,8 @@ static int require_wtmp(void) + + static int require_btmp(void) + { +- size_t i; +- for (i = 0; i < (size_t) ncolumns; i++) ++ int i; ++ for (i = 0; i < ncolumns; i++) + if (is_btmp_col(columns[i])) + return 1; + return 0; +diff --git a/sys-utils/lsipc.c b/sys-utils/lsipc.c +index 0be9d9128..1c2c4b294 100644 +--- a/sys-utils/lsipc.c ++++ b/sys-utils/lsipc.c +@@ -194,7 +194,7 @@ static const struct lsipc_coldesc coldescs[] = + * 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 size_t ncolumns; ++static int ncolumns; + + static inline size_t err_columns_index(size_t arysz, size_t idx) + { +@@ -358,7 +358,7 @@ static struct libscols_table *new_table(struct lsipc_control *ctl) + static struct libscols_table *setup_table(struct lsipc_control *ctl) + { + struct libscols_table *table = new_table(ctl); +- size_t n; ++ int n; + + for (n = 0; n < ncolumns; n++) { + int flags = coldescs[columns[n]].flag; +@@ -473,7 +473,7 @@ static void global_set_data(struct libscols_table *tb, const char *resource, + const char *desc, uintmax_t used, uintmax_t limit, int usage) + { + struct libscols_line *ln; +- size_t n; ++ int n; + + ln = scols_table_new_line(tb, NULL); + if (!ln) +@@ -545,7 +545,7 @@ static void do_sem(int id, struct lsipc_control *ctl, struct libscols_table *tb) + return; + } + for (semdsp = semds; semdsp->next != NULL || id > -1; semdsp = semdsp->next) { +- size_t n; ++ int n; + ln = scols_table_new_line(tb, NULL); + + for (n = 0; n < ncolumns; n++) { +@@ -732,7 +732,7 @@ static void do_msg(int id, struct lsipc_control *ctl, struct libscols_table *tb) + } + + for (msgdsp = msgds; msgdsp->next != NULL || id > -1 ; msgdsp = msgdsp->next) { +- size_t n; ++ int n; + ln = scols_table_new_line(tb, NULL); + + /* no need to call getpwuid() for the same user */ +@@ -888,7 +888,7 @@ static void do_shm(int id, struct lsipc_control *ctl, struct libscols_table *tb) + } + + for (shmdsp = shmds; shmdsp->next != NULL || id > -1 ; shmdsp = shmdsp->next) { +- size_t n; ++ int n; + ln = scols_table_new_line(tb, NULL); + if (!ln) + err_oom(); +@@ -1073,7 +1073,7 @@ int main(int argc, char *argv[]) + { + int opt, msg = 0, sem = 0, shm = 0, id = -1; + int show_time = 0, show_creat = 0, global = 0; +- size_t i; ++ int i; + struct lsipc_control *ctl = xcalloc(1, sizeof(struct lsipc_control)); + static struct libscols_table *tb; + char *outarg = NULL; +@@ -1248,7 +1248,7 @@ int main(int argc, char *argv[]) + + if (ctl->outmode == OUT_PRETTY && !(optarg || show_creat || show_time)) { + /* all columns for lsipc --<RESOURCE> --id <ID> */ +- for (ncolumns = 0, i = 0; i < ARRAY_SIZE(coldescs); i++) ++ for (ncolumns = 0, i = 0; i < (int) ARRAY_SIZE(coldescs); i++) + columns[ncolumns++] = i; + } else { + if (show_creat) { +@@ -1264,7 +1264,7 @@ int main(int argc, char *argv[]) + } + if (shm && show_time) { + /* keep "COMMAND" as last column */ +- size_t cmd = columns[ncolumns - 1] == COL_COMMAND; ++ int cmd = columns[ncolumns - 1] == COL_COMMAND; + + if (cmd) + ncolumns--; +@@ -1280,7 +1280,7 @@ int main(int argc, char *argv[]) + } + + if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), +- (int *) &ncolumns, column_name_to_id) < 0) ++ &ncolumns, column_name_to_id) < 0) + return EXIT_FAILURE; + + tb = setup_table(ctl); +diff --git a/sys-utils/lsmem.c b/sys-utils/lsmem.c +index aaf7374fc..4f682691d 100644 +--- a/sys-utils/lsmem.c ++++ b/sys-utils/lsmem.c +@@ -138,7 +138,7 @@ static struct coldesc coldescs[] = { + * 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 size_t ncolumns; ++static int ncolumns; + + static inline size_t err_columns_index(size_t arysz, size_t idx) + { +@@ -183,7 +183,7 @@ static int column_name_to_id(const char *name, size_t namesz) + static inline int get_column_id(int num) + { + assert(num >= 0); +- assert((size_t) num < ncolumns); ++ assert(num < ncolumns); + assert(columns[num] < (int) ARRAY_SIZE(coldescs)); + + return columns[num]; +@@ -230,7 +230,7 @@ static void set_split_policy(struct lsmem *l, int cols[], size_t ncols) + + static void add_scols_line(struct lsmem *lsmem, struct memory_block *blk) + { +- size_t i; ++ int i; + struct libscols_line *line; + + line = scols_table_new_line(lsmem->table, NULL); +@@ -523,8 +523,7 @@ int main(int argc, char **argv) + }, *lsmem = &_lsmem; + + const char *outarg = NULL, *splitarg = NULL; +- int c; +- size_t i; ++ int c, i; + + enum { + LSMEM_OPT_SUMARRY = CHAR_MAX + 1 +@@ -640,7 +639,7 @@ int main(int argc, char **argv) + } + + if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), +- (int *) &ncolumns, column_name_to_id) < 0) ++ &ncolumns, column_name_to_id) < 0) + return EXIT_FAILURE; + + /* +diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c +index d32756508..74f04dc12 100644 +--- a/sys-utils/lsns.c ++++ b/sys-utils/lsns.c +@@ -95,7 +95,7 @@ static const struct colinfo infos[] = { + }; + + static int columns[ARRAY_SIZE(infos) * 2]; +-static size_t ncolumns; ++static int ncolumns; + + enum { + LSNS_ID_MNT = 0, +@@ -193,7 +193,7 @@ static int column_name_to_id(const char *name, size_t namesz) + static inline int get_column_id(int num) + { + assert(num >= 0); +- assert((size_t) num < ncolumns); ++ assert(num < ncolumns); + assert(columns[num] < (int) ARRAY_SIZE(infos)); + + return columns[num]; +@@ -444,7 +444,7 @@ static int read_namespaces(struct lsns *ls) + static void add_scols_line(struct lsns *ls, struct libscols_table *table, + struct lsns_namespace *ns, struct lsns_process *proc) + { +- size_t i; ++ int i; + struct libscols_line *line; + + assert(ns); +@@ -504,7 +504,7 @@ static void add_scols_line(struct lsns *ls, struct libscols_table *table, + static struct libscols_table *init_scols_table(struct lsns *ls) + { + struct libscols_table *tab; +- size_t i; ++ int i; + + tab = scols_new_table(); + if (!tab) { +@@ -745,7 +745,7 @@ int main(int argc, char *argv[]) + } + + if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), +- (int *) &ncolumns, column_name_to_id) < 0) ++ &ncolumns, column_name_to_id) < 0) + return EXIT_FAILURE; + + scols_init_debug(0); +diff --git a/sys-utils/wdctl.c b/sys-utils/wdctl.c +index 24ec770fb..5e996d0ba 100644 +--- a/sys-utils/wdctl.c ++++ b/sys-utils/wdctl.c +@@ -238,7 +238,7 @@ static void add_flag_line(struct tt *tt, struct wdinfo *wd, const struct wdflag + + static int show_flags(struct wdinfo *wd, int tt_flags, uint32_t wanted) + { +- size_t i; ++ int i; + int rc = -1; + struct tt *tt; + uint32_t flags; +@@ -251,7 +251,7 @@ static int show_flags(struct wdinfo *wd, int tt_flags, uint32_t wanted) + } + + /* define columns */ +- for (i = 0; i < (size_t) ncolumns; i++) { ++ for (i = 0; i < ncolumns; i++) { + struct colinfo *col = get_column_info(i); + + if (!tt_define_column(tt, col->name, col->whint, col->flags)) { +@@ -264,7 +264,7 @@ static int show_flags(struct wdinfo *wd, int tt_flags, uint32_t wanted) + * -- one line for each supported flag (option) */ + flags = wd->ident.options; + +- for (i = 0; i < ARRAY_SIZE(wdflags); i++) { ++ for (i = 0; i < (int) ARRAY_SIZE(wdflags); i++) { + if (wanted && !(wanted & wdflags[i].flag)) + ; /* ignore */ + else if (flags & wdflags[i].flag) +@@ -513,6 +513,7 @@ int main(int argc, char *argv[]) + return EXIT_SUCCESS; + case 'h': + usage(stdout); ++ /* fallthrough */ + case 'F': + noflags = 1; + break; +diff --git a/sys-utils/zramctl.c b/sys-utils/zramctl.c +index c3112d68a..3a558d150 100644 +--- a/sys-utils/zramctl.c ++++ b/sys-utils/zramctl.c +@@ -392,7 +392,7 @@ static void fill_table_row(struct libscols_table *tb, struct zram *z) + { + static struct libscols_line *ln; + struct sysfs_cxt *sysfs; +- size_t i; ++ int i; + uint64_t num; + + assert(tb); +@@ -408,7 +408,7 @@ static void fill_table_row(struct libscols_table *tb, struct zram *z) + if (!ln) + err(EXIT_FAILURE, _("failed to initialize output line")); + +- for (i = 0; i < (size_t) ncolumns; i++) { ++ for (i = 0; i < ncolumns; i++) { + char *str = NULL; + + switch (get_column_id(i)) { +@@ -479,7 +479,7 @@ static void fill_table_row(struct libscols_table *tb, struct zram *z) + static void status(struct zram *z) + { + struct libscols_table *tb; +- size_t i; ++ int i; + + scols_init_debug(0); + +@@ -490,7 +490,7 @@ static void status(struct zram *z) + scols_table_enable_raw(tb, raw); + scols_table_enable_noheadings(tb, no_headings); + +- for (i = 0; i < (size_t) ncolumns; i++) { ++ for (i = 0; i < ncolumns; i++) { + const struct colinfo *col = get_column_info(i); + + if (!scols_table_new_column(tb, col->name, col->whint, col->flags)) +-- +2.21.0 + diff --git a/SOURCES/0188-setarch-use-personality-system-call-when-it-is-avail.patch b/SOURCES/0188-setarch-use-personality-system-call-when-it-is-avail.patch new file mode 100644 index 0000000..9b1f499 --- /dev/null +++ b/SOURCES/0188-setarch-use-personality-system-call-when-it-is-avail.patch @@ -0,0 +1,53 @@ +From bbea32299156308ba51a447dc775f320fe3c459b Mon Sep 17 00:00:00 2001 +From: Sami Kerola <kerolasa@iki.fi> +Date: Sun, 7 Sep 2014 00:16:18 +0100 +Subject: [PATCH 188/189] setarch: use personality() system call when it is + available + +Signed-off-by: Sami Kerola <kerolasa@iki.fi> +Signed-off-by: Karel Zak <kzak@redhat.com> +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1690102 +Upstream: http://github.com/karelzak/util-linux/commit/9ed11cc260a28a64de0c1fa5d94d7cd6273781a5 +--- + sys-utils/setarch.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/sys-utils/setarch.c b/sys-utils/setarch.c +index b03406dd3..bcde78f4c 100644 +--- a/sys-utils/setarch.c ++++ b/sys-utils/setarch.c +@@ -24,8 +24,7 @@ + * sparc32 util by Jakub Jelinek (1998, 1999) + */ + +-#include <syscall.h> +-#include <linux/personality.h> ++#include <sys/personality.h> + #include <unistd.h> + #include <stdio.h> + #include <string.h> +@@ -37,7 +36,11 @@ + #include "c.h" + #include "closestream.h" + +-#define set_pers(pers) ((long)syscall(SYS_personality, pers)) ++#ifndef HAVE_PERSONALITY ++# include <syscall.h> ++# define personality(pers) ((long)syscall(SYS_personality, pers)) ++#endif ++ + + /* Options without equivalent short options */ + enum { +@@ -220,7 +223,7 @@ set_arch(const char *pers, unsigned long options, int list) + errx(EXIT_FAILURE, _("%s: Unrecognized architecture"), pers); + + pers_value = transitions[i].perval | options; +- if (set_pers(pers_value) == -EINVAL) ++ if (personality(pers_value) == -EINVAL) + return 1; + + uname(&un); +-- +2.21.0 + diff --git a/SOURCES/0189-setarch-fix-personality-syscall-return-code-check.patch b/SOURCES/0189-setarch-fix-personality-syscall-return-code-check.patch new file mode 100644 index 0000000..d27aee7 --- /dev/null +++ b/SOURCES/0189-setarch-fix-personality-syscall-return-code-check.patch @@ -0,0 +1,51 @@ +From 123ac3f91e1f47108d74e5f294e0f7b6b5ba6033 Mon Sep 17 00:00:00 2001 +From: "Dmitry V. Levin" <ldv@altlinux.org> +Date: Sat, 5 Mar 2016 00:22:52 +0300 +Subject: [PATCH 189/189] setarch: fix personality syscall return code check + +Depending on architecture and kernel version, personality +syscall is either capable or incapable of returning an error. +If the return value is not an error, then it's the previous +personality value, which can be an arbitrary value +undistinguishable from an error value. +To make things clear, a second call is needed. + +For more details about personality syscall peculiarities see +https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=glibc-2.22-637-ge0043e17dfc5 + +Signed-off-by: Dmitry V. Levin <ldv@altlinux.org> +Signed-off-by: Karel Zak <kzak@redhat.com> +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1690102 +Upstream: http://github.com/karelzak/util-linux/commit/ae7065760d9bbe776a93a73d88e85c7796acb8cc +--- + sys-utils/setarch.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/sys-utils/setarch.c b/sys-utils/setarch.c +index bcde78f4c..0e45cfef9 100644 +--- a/sys-utils/setarch.c ++++ b/sys-utils/setarch.c +@@ -223,8 +223,18 @@ set_arch(const char *pers, unsigned long options, int list) + errx(EXIT_FAILURE, _("%s: Unrecognized architecture"), pers); + + pers_value = transitions[i].perval | options; +- if (personality(pers_value) == -EINVAL) +- return 1; ++ if (personality(pers_value) < 0) { ++ /* ++ * Depending on architecture and kernel version, personality ++ * syscall is either capable or incapable of returning an error. ++ * If the return value is not an error, then it's the previous ++ * personality value, which can be an arbitrary value ++ * undistinguishable from an error value. ++ * To make things clear, a second call is needed. ++ */ ++ if (personality(pers_value) < 0) ++ return 1; ++ } + + uname(&un); + if(transitions[i].result_arch && +-- +2.21.0 + diff --git a/SOURCES/0190-build-sys-check-for-librtas.patch b/SOURCES/0190-build-sys-check-for-librtas.patch new file mode 100644 index 0000000..7b14163 --- /dev/null +++ b/SOURCES/0190-build-sys-check-for-librtas.patch @@ -0,0 +1,33 @@ +From b4861ab42477e0c4c417b53412a653d24baee482 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 6 Aug 2019 12:17:38 +0200 +Subject: [PATCH] build-sys: check for librtas + +Signed-off-by: Karel Zak <kzak@redhat.com> +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1734261 +--- + configure.ac | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/configure.ac b/configure.ac +index 7381a9e5e..4c5d098e6 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -360,6 +360,14 @@ AC_CHECK_FUNCS([openat fstatat unlinkat], [have_openat=yes], [have_openat=no]) + AC_CHECK_FUNCS([ioperm iopl], [have_io=yes]) + AC_CHECK_FUNCS([open_memstream], [have_open_memstream=yes],[have_open_memstream=no]) + ++ ++AC_CHECK_LIB([rtas], [rtas_get_sysparm], [ ++ RTAS_LIBS="-lrtas" ++ AC_DEFINE_UNQUOTED([HAVE_LIBRTAS], [1], [Define if librtas exists]), [], ++]) ++AC_SUBST([RTAS_LIBS]) ++ ++ + AC_CHECK_MEMBER(struct sockaddr.sa_len, + AC_DEFINE_UNQUOTED(HAVE_SA_LEN,1,[Define if struct sockaddr contains sa_len]),, + [#include <sys/types.h> +-- +2.21.0 + diff --git a/SOURCES/0191-setarch-prefer-preprocessor-rather-than-autotools-ch.patch b/SOURCES/0191-setarch-prefer-preprocessor-rather-than-autotools-ch.patch new file mode 100644 index 0000000..1ee286b --- /dev/null +++ b/SOURCES/0191-setarch-prefer-preprocessor-rather-than-autotools-ch.patch @@ -0,0 +1,101 @@ +From e08af84191b2a8ca226e1c72c27c63dcf8ac8c27 Mon Sep 17 00:00:00 2001 +From: Sami Kerola <kerolasa@iki.fi> +Date: Sun, 28 Jul 2013 22:11:16 +0100 +Subject: [PATCH] setarch: prefer preprocessor rather than autotools check + +It seems to be pointless to spend time in ./configure phase when +preprocessor has to perform #ifndef check anyway. + +[kzak@redhat.com: - + Note that on RHEL-7 glibc' <sys/personality.h> does not contain the + same set of the flags like <linux/personality.h>. We need to include + glibc version dues to syscall, so it seems better make our macro + fallbacks more robust.] + +Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1690102 +Upstream: http://github.com/karelzak/util-linux/commit/f29a3d1868c847ae41bc681db58bd087fc9f941d +Signed-off-by: Sami Kerola <kerolasa@iki.fi> +--- + configure.ac | 12 ------------ + sys-utils/setarch.c | 22 +++++++++++----------- + 2 files changed, 11 insertions(+), 23 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 4c5d098e6..49de6c6b3 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -260,18 +260,6 @@ AC_CHECK_MEMBERS([struct termios.c_line],,, + AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec],,, + [#include <sys/stat.h>]) + +-AC_CHECK_DECLS([ +- UNAME26, +- ADDR_NO_RANDOMIZE, +- FDPIC_FUNCPTRS, +- MMAP_PAGE_ZERO, +- ADDR_COMPAT_LAYOUT, +- READ_IMPLIES_EXEC, +- ADDR_LIMIT_32BIT, +- WHOLE_SECONDS, +- STICKY_TIMEOUTS, +- ADDR_LIMIT_3GB], [], [], [#include <linux/personality.h>]) +- + AC_COMPILE_IFELSE([AC_LANG_PROGRAM( + [[ + #ifdef HAVE_SYS_SWAP_H +diff --git a/sys-utils/setarch.c b/sys-utils/setarch.c +index 0e45cfef9..9863cb298 100644 +--- a/sys-utils/setarch.c ++++ b/sys-utils/setarch.c +@@ -56,37 +56,37 @@ enum { + } while(0) + + +-#if !HAVE_DECL_UNAME26 ++#ifndef UNAME26 + # define UNAME26 0x0020000 + #endif +-#if !HAVE_DECL_ADDR_NO_RANDOMIZE ++#ifndef ADDR_NO_RANDOMIZE + # define ADDR_NO_RANDOMIZE 0x0040000 + #endif +-#if !HAVE_DECL_FDPIC_FUNCPTRS ++#ifndef FDPIC_FUNCPTRS + # define FDPIC_FUNCPTRS 0x0080000 + #endif +-#if !HAVE_DECL_MMAP_PAGE_ZERO ++#ifndef MMAP_PAGE_ZERO + # define MMAP_PAGE_ZERO 0x0100000 + #endif +-#if !HAVE_DECL_ADDR_COMPAT_LAYOUT ++#ifndef ADDR_COMPAT_LAYOUT + # define ADDR_COMPAT_LAYOUT 0x0200000 + #endif +-#if !HAVE_DECL_READ_IMPLIES_EXEC ++#ifndef READ_IMPLIES_EXEC + # define READ_IMPLIES_EXEC 0x0400000 + #endif +-#if !HAVE_DECL_ADDR_LIMIT_32BIT ++#ifndef ADDR_LIMIT_32BIT + # define ADDR_LIMIT_32BIT 0x0800000 + #endif +-#if !HAVE_DECL_SHORT_INODE ++#ifndef SHORT_INODE + # define SHORT_INODE 0x1000000 + #endif +-#if !HAVE_DECL_WHOLE_SECONDS ++#ifndef WHOLE_SECONDS + # define WHOLE_SECONDS 0x2000000 + #endif +-#if !HAVE_DECL_STICKY_TIMEOUTS ++#ifndef STICKY_TIMEOUTS + # define STICKY_TIMEOUTS 0x4000000 + #endif +-#if !HAVE_DECL_ADDR_LIMIT_3GB ++#ifndef ADDR_LIMIT_3GB + # define ADDR_LIMIT_3GB 0x8000000 + #endif + +-- +2.21.0 + diff --git a/SOURCES/0192-libmount-Preserve-empty-string-value-in-optstr-parsi.patch b/SOURCES/0192-libmount-Preserve-empty-string-value-in-optstr-parsi.patch new file mode 100644 index 0000000..af16b0a --- /dev/null +++ b/SOURCES/0192-libmount-Preserve-empty-string-value-in-optstr-parsi.patch @@ -0,0 +1,207 @@ +From c0f53628926e9fe8956ea276e30d98817d8cbcd4 Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger <filbranden@google.com> +Date: Wed, 10 Aug 2016 13:27:07 -0700 +Subject: [PATCH] libmount: Preserve empty string value in optstr parsing + +Recent mount (since the switch to libmount in v2.22) drops the '=' in +mount options that are set to an empty value. For example, the command +line below will be affected: + + # mount -o rw,myopt='' -t tmpfs tmpfs /mnt/tmp + +Fix that by preserving an empty string in the options passed to the +mount(2) syscall when they are present on the command line. + +Add test cases to ensure empty string handling is working as expected +and in order to prevent regressions in the future. + +Also tested manually by stracing mount commands (on a kernel which +accepts a special extra option, for testing purposes.) + +Before this commit: + + # strace -e mount ./mount -t tmpfs -o rw,myopt='' tmpfs /mnt/tmp + mount("tmpfs", "/mnt/tmp", "tmpfs", MS_MGC_VAL, "myarg") = -1 EINVAL (Invalid argument) + +After this commit: + + # strace -e mount ./mount -t tmpfs -o rw,myopt='' tmpfs /mnt/tmp + mount("tmpfs", "/mnt/tmp", "tmpfs", MS_MGC_VAL, "myopt=") = 0 + +All test cases pass, including newly added test cases. Also checked +them with valgrind using: + + $ tests/run.sh --memcheck libmount/optstr + +Fixes #332. + +Signed-off-by: Filipe Brandenburger <filbranden@google.com> +Signed-off-by: Karel Zak <kzak@redhat.com> +Upstream: http://github.com/karelzak/util-linux/commit/727c689908c5e68c92aa1dd65e0d3bdb6d91c1e5 +Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1740572 +--- + libmount/src/optstr.c | 4 +-- + .../libmount/optstr-append-empty-value | 1 + + .../libmount/optstr-deduplicate-empty | 1 + + .../libmount/optstr-prepend-empty-value | 1 + + .../libmount/optstr-remove-empty-value | 1 + + tests/expected/libmount/optstr-set-empty | 1 + + tests/expected/libmount/optstr-set-new-empty | 1 + + .../libmount/optstr-set-new-end-empty | 1 + + tests/ts/libmount/optstr | 28 +++++++++++++++++++ + 9 files changed, 37 insertions(+), 2 deletions(-) + create mode 100644 tests/expected/libmount/optstr-append-empty-value + create mode 100644 tests/expected/libmount/optstr-deduplicate-empty + create mode 100644 tests/expected/libmount/optstr-prepend-empty-value + create mode 100644 tests/expected/libmount/optstr-remove-empty-value + create mode 100644 tests/expected/libmount/optstr-set-empty + create mode 100644 tests/expected/libmount/optstr-set-new-empty + create mode 100644 tests/expected/libmount/optstr-set-new-end-empty + +diff --git a/libmount/src/optstr.c b/libmount/src/optstr.c +index 3c680ff6e..7bd9bbdb5 100644 +--- a/libmount/src/optstr.c ++++ b/libmount/src/optstr.c +@@ -186,7 +186,7 @@ static int __mnt_optstr_append_option(char **optstr, + sz = osz + nsz + 1; /* 1: '\0' */ + if (osz) + sz++; /* ',' options separator */ +- if (vsz) ++ if (value) + sz += vsz + 1; /* 1: '=' */ + + p = realloc(*optstr, sz); +@@ -202,7 +202,7 @@ static int __mnt_optstr_append_option(char **optstr, + memcpy(p, name, nsz); + p += nsz; + +- if (vsz) { ++ if (value) { + *p++ = '='; + memcpy(p, value, vsz); + p += vsz; +diff --git a/tests/expected/libmount/optstr-append-empty-value b/tests/expected/libmount/optstr-append-empty-value +new file mode 100644 +index 000000000..35adf5c72 +--- /dev/null ++++ b/tests/expected/libmount/optstr-append-empty-value +@@ -0,0 +1 @@ ++result: >aaa,bbb=BBB,ccc,ddd=< +diff --git a/tests/expected/libmount/optstr-deduplicate-empty b/tests/expected/libmount/optstr-deduplicate-empty +new file mode 100644 +index 000000000..63b74f678 +--- /dev/null ++++ b/tests/expected/libmount/optstr-deduplicate-empty +@@ -0,0 +1 @@ ++result: >bbb,ccc,xxx,ddd,AAA=,fff=eee< +diff --git a/tests/expected/libmount/optstr-prepend-empty-value b/tests/expected/libmount/optstr-prepend-empty-value +new file mode 100644 +index 000000000..4cea63527 +--- /dev/null ++++ b/tests/expected/libmount/optstr-prepend-empty-value +@@ -0,0 +1 @@ ++result: >ddd=,aaa,bbb=BBB,ccc< +diff --git a/tests/expected/libmount/optstr-remove-empty-value b/tests/expected/libmount/optstr-remove-empty-value +new file mode 100644 +index 000000000..eee5c95b9 +--- /dev/null ++++ b/tests/expected/libmount/optstr-remove-empty-value +@@ -0,0 +1 @@ ++result: >aaa,ccc< +diff --git a/tests/expected/libmount/optstr-set-empty b/tests/expected/libmount/optstr-set-empty +new file mode 100644 +index 000000000..e0a3300f9 +--- /dev/null ++++ b/tests/expected/libmount/optstr-set-empty +@@ -0,0 +1 @@ ++result: >aaa,bbb=,ccc< +diff --git a/tests/expected/libmount/optstr-set-new-empty b/tests/expected/libmount/optstr-set-new-empty +new file mode 100644 +index 000000000..a1cfb3721 +--- /dev/null ++++ b/tests/expected/libmount/optstr-set-new-empty +@@ -0,0 +1 @@ ++result: >aaa=,bbb=BBB,ccc< +diff --git a/tests/expected/libmount/optstr-set-new-end-empty b/tests/expected/libmount/optstr-set-new-end-empty +new file mode 100644 +index 000000000..d0e9880f3 +--- /dev/null ++++ b/tests/expected/libmount/optstr-set-new-end-empty +@@ -0,0 +1 @@ ++result: >aaa,bbb=BBB,ccc=< +diff --git a/tests/ts/libmount/optstr b/tests/ts/libmount/optstr +index f6a5c0530..07a548963 100755 +--- a/tests/ts/libmount/optstr ++++ b/tests/ts/libmount/optstr +@@ -20,6 +20,10 @@ ts_init_subtest "append-value" + ts_valgrind $TESTPROG --append "aaa,bbb=BBB,ccc" "ddd" "DDD" &> $TS_OUTPUT + ts_finalize_subtest + ++ts_init_subtest "append-empty-value" ++ts_valgrind $TESTPROG --append "aaa,bbb=BBB,ccc" "ddd" "" &> $TS_OUTPUT ++ts_finalize_subtest ++ + ts_init_subtest "prepend" + ts_valgrind $TESTPROG --prepend "aaa,bbb=BBB,ccc" "ddd" &> $TS_OUTPUT + ts_finalize_subtest +@@ -28,6 +32,10 @@ ts_init_subtest "prepend-value" + ts_valgrind $TESTPROG --prepend "aaa,bbb=BBB,ccc" "ddd" "DDD" &> $TS_OUTPUT + ts_finalize_subtest + ++ts_init_subtest "prepend-empty-value" ++ts_valgrind $TESTPROG --prepend "aaa,bbb=BBB,ccc" "ddd" "" &> $TS_OUTPUT ++ts_finalize_subtest ++ + ts_init_subtest "set-remove" + ts_valgrind $TESTPROG --set "aaa,bbb=BBB,ccc" "bbb" &> $TS_OUTPUT + ts_finalize_subtest +@@ -40,14 +48,26 @@ ts_init_subtest "set-large" + ts_valgrind $TESTPROG --set "aaa,bbb=BBB,ccc" "bbb" "XXX-YYY-ZZZ" &> $TS_OUTPUT + ts_finalize_subtest + ++ts_init_subtest "set-empty" ++ts_valgrind $TESTPROG --set "aaa,bbb=BBB,ccc" "bbb" "" &> $TS_OUTPUT ++ts_finalize_subtest ++ + ts_init_subtest "set-new" + ts_valgrind $TESTPROG --set "aaa,bbb=BBB,ccc" "aaa" "XXX" &> $TS_OUTPUT + ts_finalize_subtest + ++ts_init_subtest "set-new-empty" ++ts_valgrind $TESTPROG --set "aaa,bbb=BBB,ccc" "aaa" "" &> $TS_OUTPUT ++ts_finalize_subtest ++ + ts_init_subtest "set-new-end" + ts_valgrind $TESTPROG --set "aaa,bbb=BBB,ccc" "ccc" "XXX" &> $TS_OUTPUT + ts_finalize_subtest + ++ts_init_subtest "set-new-end-empty" ++ts_valgrind $TESTPROG --set "aaa,bbb=BBB,ccc" "ccc" "" &> $TS_OUTPUT ++ts_finalize_subtest ++ + ts_init_subtest "get" + ts_valgrind $TESTPROG --get "aaa,bbb=BBB,ccc" "aaa" &> $TS_OUTPUT + ts_finalize_subtest +@@ -68,6 +88,10 @@ ts_init_subtest "remove-value" + ts_valgrind $TESTPROG --remove "aaa,bbb=BBB,ccc" "bbb" &> $TS_OUTPUT + ts_finalize_subtest + ++ts_init_subtest "remove-empty-value" ++ts_valgrind $TESTPROG --remove "aaa,bbb=,ccc" "bbb" &> $TS_OUTPUT ++ts_finalize_subtest ++ + ts_init_subtest "split" + ts_valgrind $TESTPROG --split "aaa,bbb=BBB,ccc,x-bar,x-foo=foodata,user=kzak,noexec,nosuid,loop=/dev/loop0" &> $TS_OUTPUT + ts_finalize_subtest +@@ -92,4 +116,8 @@ ts_init_subtest "deduplicate" + ts_valgrind $TESTPROG --dedup bbb,ccc,AAA,xxx,AAA=a,AAA=bbb,ddd,AAA=ccc,fff=eee AAA &> $TS_OUTPUT + ts_finalize_subtest + ++ts_init_subtest "deduplicate-empty" ++ts_valgrind $TESTPROG --dedup bbb,ccc,AAA,xxx,AAA=a,AAA=bbb,ddd,AAA=,fff=eee AAA &> $TS_OUTPUT ++ts_finalize_subtest ++ + ts_finalize +-- +2.21.0 + 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 <ctype.h> /* for isdigit() */ + #include <unistd.h> + #include <signal.h> ++#include <errno.h> + + #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.23-login-lastlog-create.patch b/SOURCES/2.23-login-lastlog-create.patch new file mode 100644 index 0000000..839193a --- /dev/null +++ b/SOURCES/2.23-login-lastlog-create.patch @@ -0,0 +1,12 @@ +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 2013-07-30 10:39:26.222738397 +0200 ++++ util-linux-2.23.2/login-utils/login.c 2013-09-09 09:01:39.923225757 +0200 +@@ -502,7 +502,7 @@ static void log_lastlog(struct login_con + if (!cxt->pwd) + return; + +- fd = open(_PATH_LASTLOG, O_RDWR, 0); ++ fd = open(_PATH_LASTLOG, O_RDWR | O_CREAT, 0); + if (fd < 0) + return; + diff --git a/SOURCES/2.24-agetty-clocal.patch b/SOURCES/2.24-agetty-clocal.patch new file mode 100644 index 0000000..22fe361 --- /dev/null +++ b/SOURCES/2.24-agetty-clocal.patch @@ -0,0 +1,149 @@ +diff -up util-linux-2.23.2/term-utils/agetty.c.kzak util-linux-2.23.2/term-utils/agetty.c +--- util-linux-2.23.2/term-utils/agetty.c.kzak 2013-07-30 11:14:18.124912322 +0200 ++++ util-linux-2.23.2/term-utils/agetty.c 2013-09-09 09:07:46.406689270 +0200 +@@ -132,13 +132,20 @@ struct options { + int delay; /* Sleep seconds before prompt */ + int nice; /* Run login with this priority */ + int numspeed; /* number of baud rates to try */ ++ int clocal; /* CLOCAL_MODE_* */ + speed_t speeds[MAX_SPEED]; /* baud rates to be tried */ + }; + ++enum { ++ CLOCAL_MODE_AUTO = 0, ++ CLOCAL_MODE_ALWAYS, ++ CLOCAL_MODE_NEVER ++}; ++ + #define F_PARSE (1<<0) /* process modem status messages */ + #define F_ISSUE (1<<1) /* display /etc/issue */ + #define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */ +-#define F_LOCAL (1<<3) /* force local */ ++ + #define F_INITSTRING (1<<4) /* initstring is set */ + #define F_WAITCRLF (1<<5) /* wait for CR or LF */ + #define F_CUSTISSUE (1<<6) /* give alternative issue file */ +@@ -235,10 +242,13 @@ static void login_options_to_argv(char * + static char *fakehost; + + #ifdef DEBUGGING +-#define debug(s) do { fprintf(dbf,s); fflush(dbf); } while (0) ++# ifndef DEBUG_OUTPUT ++# define DEBUG_OUTPUT "/dev/ttyp0" ++# endif ++# define debug(s) do { fprintf(dbf,s); fflush(dbf); } while (0) + FILE *dbf; + #else +-#define debug(s) do { ; } while (0) ++# define debug(s) do { ; } while (0) + #endif + + int main(int argc, char **argv) +@@ -270,7 +280,7 @@ int main(int argc, char **argv) + sigaction(SIGINT, &sa, &sa_int); + + #ifdef DEBUGGING +- dbf = fopen("/dev/ttyp0", "w"); ++ dbf = fopen(DEBUG_OUTPUT, "w"); + for (int i = 1; i < argc; i++) + debug(argv[i]); + #endif /* DEBUGGING */ +@@ -311,8 +321,10 @@ int main(int argc, char **argv) + strlen(options.initstring)); + } + +- if (!serial_tty_option(&options, F_LOCAL)) +- /* Go to blocking write mode unless -L is specified. */ ++ if (options.flags & F_VCONSOLE || options.clocal != CLOCAL_MODE_ALWAYS) ++ /* Go to blocking mode unless -L is specified, this change ++ * affects stdout, stdin and stderr as all the file descriptors ++ * are created by dup(). */ + fcntl(STDOUT_FILENO, F_SETFL, + fcntl(STDOUT_FILENO, F_GETFL, 0) & ~O_NONBLOCK); + +@@ -420,6 +432,12 @@ int main(int argc, char **argv) + options.tty); + } + ++#ifdef DEBUGGING ++ fprintf(dbf, "read %c\n", ch); ++ if (close_stream(dbf) != 0) ++ log_err("write failed: %s", DEBUG_OUTPUT); ++#endif ++ + /* Let the login program take care of password validation. */ + execv(options.login, login_argv); + log_err(_("%s: can't exec %s: %m"), options.tty, login_argv[0]); +@@ -534,7 +552,7 @@ static void parse_args(int argc, char ** + { "init-string", required_argument, 0, 'I' }, + { "noclear", no_argument, 0, 'J' }, + { "login-program", required_argument, 0, 'l' }, +- { "local-line", no_argument, 0, 'L' }, ++ { "local-line", optional_argument, 0, 'L' }, + { "extract-baud", no_argument, 0, 'm' }, + { "skip-login", no_argument, 0, 'n' }, + { "nonewline", no_argument, 0, 'N' }, +@@ -603,7 +621,18 @@ static void parse_args(int argc, char ** + op->login = optarg; + break; + case 'L': +- op->flags |= F_LOCAL; ++ /* -L and -L=always have the same meaning */ ++ op->clocal = CLOCAL_MODE_ALWAYS; ++ if (optarg) { ++ if (strcmp(optarg, "=always") == 0) ++ op->clocal = CLOCAL_MODE_ALWAYS; ++ else if (strcmp(optarg, "=never") == 0) ++ op->clocal = CLOCAL_MODE_NEVER; ++ else if (strcmp(optarg, "=auto") == 0) ++ op->clocal = CLOCAL_MODE_AUTO; ++ else ++ log_err(_("unssuported --local-line mode argument")); ++ } + break; + case 'm': + op->flags |= F_PARSE; +@@ -1090,8 +1119,19 @@ static void termio_init(struct options * + cfsetispeed(tp, ispeed); + cfsetospeed(tp, ospeed); + +- if (op->flags & F_LOCAL) +- tp->c_cflag |= CLOCAL; ++ /* The default is to follow setting from kernel, but it's possible ++ * to explicitly remove/add CLOCAL flag by -L[=<mode>]*/ ++ switch (op->clocal) { ++ case CLOCAL_MODE_ALWAYS: ++ tp->c_cflag |= CLOCAL; /* -L or -L=always */ ++ break; ++ case CLOCAL_MODE_NEVER: ++ tp->c_cflag &= ~CLOCAL; /* -L=never */ ++ break; ++ case CLOCAL_MODE_AUTO: /* -L=auto */ ++ break; ++ } ++ + #ifdef HAVE_STRUCT_TERMIOS_C_LINE + tp->c_line = 0; + #endif +@@ -1412,9 +1452,10 @@ static char *get_logname(struct options + + if (read(STDIN_FILENO, &c, 1) < 1) { + +- /* Do not report trivial like EINTR/EIO errors. */ ++ /* The terminal could be open with O_NONBLOCK when ++ * -L (force CLOCAL) is specified... */ + if (errno == EINTR || errno == EAGAIN) { +- usleep(1000); ++ usleep(250000); + continue; + } + switch (errno) { +@@ -1648,7 +1689,7 @@ static void __attribute__ ((__noreturn__ + fputs(_(" -i, --noissue do not display issue file\n"), out); + fputs(_(" -I, --init-string <string> set init string\n"), out); + fputs(_(" -l, --login-program <file> specify login program\n"), out); +- fputs(_(" -L, --local-line force local line\n"), out); ++ fputs(_(" -L, --local-line[=<mode>] cotrol local line flag\n"), out); + fputs(_(" -m, --extract-baud extract baud rate during connect\n"), out); + fputs(_(" -n, --skip-login do not prompt for login\n"), out); + fputs(_(" -o, --login-options <opts> options that are passed to login\n"), out); diff --git a/SOURCES/2.24-agetty-etc-os-release.patch b/SOURCES/2.24-agetty-etc-os-release.patch new file mode 100644 index 0000000..43b901a --- /dev/null +++ b/SOURCES/2.24-agetty-etc-os-release.patch @@ -0,0 +1,176 @@ +diff -up util-linux-2.23.2/include/pathnames.h.kzak util-linux-2.23.2/include/pathnames.h +--- util-linux-2.23.2/include/pathnames.h.kzak 2013-07-30 10:39:26.201738190 +0200 ++++ util-linux-2.23.2/include/pathnames.h 2013-09-12 13:05:35.928359383 +0200 +@@ -63,6 +63,7 @@ + + /* used in term-utils/agetty.c */ + #define _PATH_ISSUE "/etc/issue" ++#define _PATH_OS_RELEASE "/etc/os-release" + #define _PATH_NUMLOCK_ON _PATH_LOCALSTATEDIR "/numlock-on" + + #define _PATH_LOGINDEFS "/etc/login.defs" +diff -up util-linux-2.23.2/term-utils/agetty.8.kzak util-linux-2.23.2/term-utils/agetty.8 +--- util-linux-2.23.2/term-utils/agetty.8.kzak 2013-07-30 10:58:20.889261333 +0200 ++++ util-linux-2.23.2/term-utils/agetty.8 2013-09-12 13:05:35.928359383 +0200 +@@ -310,6 +310,14 @@ Insert the current date. + .TP + s + Insert the system name, the name of the operating system. Same as `uname \-s'. ++See also \\S escape code. ++.TP ++S or S{VARIABLE} ++Insert the VARIABLE data from \fI/etc/os-release\fP. If the VARIABLE argument ++is not specified then use PRETTY_NAME from the file or the system name (see \\s). ++This escape code allows to keep \fI/etc/issue\fP distribution and release ++independent. Note that \\S{ANSI_COLOR} is converted to the real terminal ++escape sequence. + .TP + l + Insert the name of the current tty line. +@@ -368,11 +376,14 @@ the system status file. + .B /etc/issue + printed before the login prompt. + .TP ++.B /etc/os-release ++operating system identification data. ++.TP + .B /dev/console + problem reports (if syslog(3) is not used). + .TP + .B /etc/inittab +-\fIinit\fP(8) configuration file. ++\fIinit\fP(8) configuration file for SysV-style init daemon. + .SH BUGS + .ad + .fi +diff -up util-linux-2.23.2/term-utils/agetty.c.kzak util-linux-2.23.2/term-utils/agetty.c +--- util-linux-2.23.2/term-utils/agetty.c.kzak 2013-09-12 13:05:35.927359379 +0200 ++++ util-linux-2.23.2/term-utils/agetty.c 2013-09-12 13:05:35.929359388 +0200 +@@ -129,6 +129,7 @@ struct options { + char *issue; /* alternative issue file */ + char *erasechars; /* string with erase chars */ + char *killchars; /* string with kill chars */ ++ char *osrelease; /* /etc/os-release data */ + int delay; /* Sleep seconds before prompt */ + int nice; /* Run login with this priority */ + int numspeed; /* number of baud rates to try */ +@@ -431,7 +432,8 @@ int main(int argc, char **argv) + log_warn(_("%s: can't change process priority: %m"), + options.tty); + } +- ++ if (options.osrelease) ++ free(options.osrelease); + #ifdef DEBUGGING + fprintf(dbf, "read %c\n", ch); + if (close_stream(dbf) != 0) +@@ -1279,6 +1281,84 @@ static char *xgetdomainname(void) + return NULL; + } + ++static char *read_os_release(struct options *op, const char *varname) ++{ ++ int fd = -1; ++ struct stat st; ++ size_t varsz = strlen(varname); ++ char *p, *buf = NULL, *ret = NULL; ++ ++ /* read the file only once */ ++ if (!op->osrelease) { ++ fd = open(_PATH_OS_RELEASE, O_RDONLY); ++ if (fd == -1) { ++ log_warn(_("cannot open: %s: %m"), _PATH_OS_RELEASE); ++ return NULL; ++ } ++ ++ if (fstat(fd, &st) < 0 || st.st_size > 4 * 1024 * 1024) ++ goto done; ++ ++ op->osrelease = malloc(st.st_size + 1); ++ if (!op->osrelease) ++ log_err(_("failed to allocate memory: %m")); ++ if (read_all(fd, op->osrelease, st.st_size) != (ssize_t) st.st_size) { ++ free(op->osrelease); ++ op->osrelease = NULL; ++ goto done; ++ } ++ op->osrelease[st.st_size] = 0; ++ } ++ buf = strdup(op->osrelease); ++ if (!buf) ++ log_err(_("failed to allocate memory: %m")); ++ p = buf; ++ ++ for (;;) { ++ char *eol, *eon; ++ ++ p += strspn(p, "\n\r"); ++ p += strspn(p, " \t\n\r"); ++ if (!*p) ++ break; ++ if (strspn(p, "#;\n") != 0) { ++ p += strcspn(p, "\n\r"); ++ continue; ++ } ++ if (strncmp(p, varname, varsz) != 0) { ++ p += strcspn(p, "\n\r"); ++ continue; ++ } ++ p += varsz; ++ p += strspn(p, " \t\n\r=\""); ++ eol = p + strcspn(p, "\n\r"); ++ *eol = '\0'; ++ eon = eol-1; ++ while (eon > p) { ++ if (*eon == '\t' || *eon == ' ') { ++ eon--; ++ continue; ++ } ++ if (*eon == '"') { ++ *eon = '\0'; ++ break; ++ } ++ break; ++ } ++ if (ret) ++ free(ret); ++ ret = strdup(p); ++ if (!ret) ++ log_err(_("failed to allocate memory: %m")); ++ p = eol + 1; ++ } ++done: ++ free(buf); ++ if (fd >= 0) ++ close(fd); ++ return ret; ++} ++ + /* Show login prompt, optionally preceded by /etc/issue contents. */ + static void do_prompt(struct options *op, struct termios *tp) + { +@@ -1968,6 +2048,24 @@ static void output_special_char(unsigned + } + break; + } ++ case 'S': ++ { ++ char *var = NULL, varname[64]; ++ ++ if (get_escape_argument(fp, varname, sizeof(varname))) ++ var = read_os_release(op, varname); ++ else if (!(var = read_os_release(op, "PRETTY_NAME"))) ++ var = uts.sysname; ++ if (var) { ++ if (strcmp(varname, "ANSI_COLOR") == 0) ++ printf("\033[%sm", var); ++ else ++ printf("%s", var); ++ if (var != uts.sysname) ++ free(var); ++ } ++ break; ++ } + case 'u': + case 'U': + { diff --git a/SOURCES/2.24-blockdev-setbsz-hint.patch b/SOURCES/2.24-blockdev-setbsz-hint.patch new file mode 100644 index 0000000..6bb5957 --- /dev/null +++ b/SOURCES/2.24-blockdev-setbsz-hint.patch @@ -0,0 +1,42 @@ +From 7ab32ae64d05f018c171ba1525bc337805d84391 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Fri, 11 Oct 2013 11:16:23 +0200 +Subject: [PATCH] blockdev: add note about --setbsz usability + +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + disk-utils/blockdev.8 | 4 +++- + disk-utils/blockdev.c | 2 +- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/disk-utils/blockdev.8 b/disk-utils/blockdev.8 +index 2b3d64c..f4282da 100644 +--- a/disk-utils/blockdev.8 ++++ b/disk-utils/blockdev.8 +@@ -68,7 +68,9 @@ Get size in 512-byte sectors. + .IP "\fB\-\-rereadpt\fP" + Reread partition table + .IP "\fB\-\-setbsz\fP \fIbytes\fP" +-Set blocksize. ++Set blocksize. Note that the block size is specific to the current file ++descriptor opening the block device, so the change of block size only persists ++for as long as blockdev has the device open, and is lost once blockdev exits. + .IP "\fB\-\-setfra\fP \fIsectors\fP" + Set filesystem readahead (same like --setra on 2.6 kernels). + .IP "\fB\-\-setra\fP \fIsectors\fP" +diff --git a/disk-utils/blockdev.c b/disk-utils/blockdev.c +index 4543818..d030217 100644 +--- a/disk-utils/blockdev.c ++++ b/disk-utils/blockdev.c +@@ -127,7 +127,7 @@ static const struct bdc bdcms[] = + .argname = "<bytes>", + .argtype = ARG_INT, + .flags = FL_NORESULT, +- .help = N_("set blocksize") ++ .help = N_("set blocksize on file descriptor opening the block device") + },{ + IOCTL_ENTRY(BLKGETSIZE), + .name = "--getsize", +-- +1.8.3.1 + diff --git a/SOURCES/2.24-fsck-fstab.patch b/SOURCES/2.24-fsck-fstab.patch new file mode 100644 index 0000000..113c52b --- /dev/null +++ b/SOURCES/2.24-fsck-fstab.patch @@ -0,0 +1,22 @@ +diff -up util-linux-2.23.2/disk-utils/fsck.c.kzak util-linux-2.23.2/disk-utils/fsck.c +--- util-linux-2.23.2/disk-utils/fsck.c.kzak 2013-06-13 09:46:10.377650254 +0200 ++++ util-linux-2.23.2/disk-utils/fsck.c 2014-03-25 12:46:59.525939425 +0100 +@@ -437,10 +437,14 @@ static void load_fs_info(void) + if (mnt_table_parse_fstab(fstab, path)) { + if (!path) + path = mnt_get_fstab_path(); +- if (errno) +- warn(_("%s: failed to parse fstab"), path); +- else +- warnx(_("%s: failed to parse fstab"), path); ++ ++ /* don't print error when there is no fstab at all */ ++ if (access(path, F_OK) == 0) { ++ if (errno) ++ warn(_("%s: failed to parse fstab"), path); ++ else ++ warnx(_("%s: failed to parse fstab"), path); ++ } + } + } + diff --git a/SOURCES/2.24-libfdisk-fix-SIGFPE.patch b/SOURCES/2.24-libfdisk-fix-SIGFPE.patch new file mode 100644 index 0000000..bb8e872 --- /dev/null +++ b/SOURCES/2.24-libfdisk-fix-SIGFPE.patch @@ -0,0 +1,40 @@ +From 44baaedaffee029dca76796b933412d97a19dff6 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 9 Sep 2013 10:57:50 +0200 +Subject: [PATCH] libfdisk: fix SIGFPE + + #0 recount_geometry at libfdisk/src/alignment.c:143 + #1 fdisk_discover_geometry at libfdisk/src/alignment.c:205 + #2 fdisk_context_assign_device at libfdisk/src/context.c:173 + #3 print_partition_table_from_option at fdisks/fdisk.c:924 + +References: https://bugzilla.redhat.com/show_bug.cgi?id=1005566 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libfdisk/src/alignment.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/libfdisk/src/alignment.c b/libfdisk/src/alignment.c +index ac44e73..4d4ab48 100644 +--- a/libfdisk/src/alignment.c ++++ b/libfdisk/src/alignment.c +@@ -193,11 +193,12 @@ int fdisk_discover_geometry(struct fdisk_context *cxt) + + /* what the kernel/bios thinks the geometry is */ + blkdev_get_geometry(cxt->dev_fd, &h, &s); +- if (!h && !s) { +- /* unable to discover geometry, use default values */ +- s = 63; ++ ++ /* defaults */ ++ if (!h) + h = 255; +- } ++ if (!s) ++ s = 63; + + /* obtained heads and sectors */ + cxt->geom.heads = h; +-- +1.8.1.4 + 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-libmount-canonicalize-for-conversion-from-loopdev.patch b/SOURCES/2.24-libmount-canonicalize-for-conversion-from-loopdev.patch new file mode 100644 index 0000000..d1e2341 --- /dev/null +++ b/SOURCES/2.24-libmount-canonicalize-for-conversion-from-loopdev.patch @@ -0,0 +1,40 @@ +From 3f420a49dd4d0c413b635dd621aa34eebce2d3d2 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 5 Aug 2013 13:58:01 +0200 +Subject: [PATCH] libmount: canonicalize for conversion from loopdev backing + file + + # mount foo.img /mnt + # umount foo.img + umount: foo.img: not mounted + +The loopdev code (and sysfs backing_file) uses absolute paths, but +libmount does not canonicalize the path before lookup for the backing file. + +References: https://bugzilla.redhat.com/show_bug.cgi?id=950497 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libmount/src/context_umount.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c +index 2b791c4..b02902c 100644 +--- a/libmount/src/context_umount.c ++++ b/libmount/src/context_umount.c +@@ -161,7 +161,12 @@ try_loopdev: + struct stat st; + + if (stat(tgt, &st) == 0 && S_ISREG(st.st_mode)) { +- int count = loopdev_count_by_backing_file(tgt, &loopdev); ++ int count; ++ ++ cn_tgt = mnt_resolve_path(tgt, cache); ++ count = loopdev_count_by_backing_file(cn_tgt, &loopdev); ++ if (!cache) ++ free(cn_tgt); + if (count == 1) { + DBG(CXT, mnt_debug_h(cxt, + "umount: %s --> %s (retry)", tgt, loopdev)); +-- +1.8.1.4 + diff --git a/SOURCES/2.24-libmount-mem.patch b/SOURCES/2.24-libmount-mem.patch new file mode 100644 index 0000000..64926a0 --- /dev/null +++ b/SOURCES/2.24-libmount-mem.patch @@ -0,0 +1,24 @@ +diff -up util-linux-2.23.2/libmount/src/context_umount.c.kzak util-linux-2.23.2/libmount/src/context_umount.c +--- util-linux-2.23.2/libmount/src/context_umount.c.kzak 2013-10-07 11:43:10.990598629 +0200 ++++ util-linux-2.23.2/libmount/src/context_umount.c 2013-10-07 11:46:01.051031431 +0200 +@@ -423,6 +423,8 @@ static int evaluate_permissions(struct l + if (optstr && !mnt_optstr_get_option(optstr, + "user", &mtab_user, &sz) && sz) + ok = !strncmp(curr_user, mtab_user, sz); ++ ++ free(curr_user); + } + + if (ok) { +diff -up util-linux-2.23.2/libmount/src/utils.c.kzak util-linux-2.23.2/libmount/src/utils.c +--- util-linux-2.23.2/libmount/src/utils.c.kzak 2013-07-30 11:15:27.391515623 +0200 ++++ util-linux-2.23.2/libmount/src/utils.c 2013-10-07 11:43:27.209924834 +0200 +@@ -159,7 +159,7 @@ int mnt_chdir_to_parent(const char *targ + if (!last || !*last) + memcpy(*filename, ".", 2); + else +- memcpy(*filename, last, strlen(last) + 1); ++ memmove(*filename, last, strlen(last) + 1); + } else + free(buf); + return 0; diff --git a/SOURCES/2.24-losetup-add-device.patch b/SOURCES/2.24-losetup-add-device.patch new file mode 100644 index 0000000..680d616 --- /dev/null +++ b/SOURCES/2.24-losetup-add-device.patch @@ -0,0 +1,63 @@ +diff -up util-linux-2.23.2/include/loopdev.h.kzak util-linux-2.23.2/include/loopdev.h +--- util-linux-2.23.2/include/loopdev.h.kzak 2013-06-13 09:46:10.397650425 +0200 ++++ util-linux-2.23.2/include/loopdev.h 2014-01-14 11:11:48.427643690 +0100 +@@ -149,6 +149,7 @@ extern void loopcxt_enable_debug(struct + extern int loopcxt_set_device(struct loopdev_cxt *lc, const char *device) + __attribute__ ((warn_unused_result)); + extern int loopcxt_has_device(struct loopdev_cxt *lc); ++extern int loopcxt_add_device(struct loopdev_cxt *lc); + extern char *loopcxt_strdup_device(struct loopdev_cxt *lc); + extern const char *loopcxt_get_device(struct loopdev_cxt *lc); + extern struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc); +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 2013-07-30 11:19:20.143600300 +0200 ++++ util-linux-2.23.2/lib/loopdev.c 2014-01-14 11:11:48.428643700 +0100 +@@ -1298,6 +1298,36 @@ int loopcxt_delete_device(struct loopdev + return 0; + } + ++int loopcxt_add_device(struct loopdev_cxt *lc) ++{ ++ int rc = -EINVAL; ++ int ctl, nr = -1; ++ const char *p, *dev = loopcxt_get_device(lc); ++ ++ if (!dev) ++ goto done; ++ ++ if (!(lc->flags & LOOPDEV_FL_CONTROL)) { ++ rc = -ENOSYS; ++ goto done; ++ } ++ ++ p = strrchr(dev, '/'); ++ if (!p || (sscanf(p, "/loop%d", &nr) != 1 && sscanf(p, "/%d", &nr) != 1) ++ || nr < 0) ++ goto done; ++ ++ ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC); ++ if (ctl >= 0) { ++ DBG(lc, loopdev_debug("add_device %d", nr)); ++ rc = ioctl(ctl, LOOP_CTL_ADD, nr); ++ close(ctl); ++ } ++done: ++ DBG(lc, loopdev_debug("add_device done [rc=%d]", rc)); ++ return rc; ++} ++ + /* + * Note that LOOP_CTL_GET_FREE ioctl is supported since kernel 3.1. In older + * kernels we have to check all loop devices to found unused one. +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 2013-07-30 11:20:16.987117853 +0200 ++++ util-linux-2.23.2/sys-utils/losetup.c 2014-01-14 11:11:48.428643700 +0100 +@@ -600,6 +600,8 @@ int main(int argc, char **argv) + { + int hasdev = loopcxt_has_device(&lc); + ++ if (hasdev && !is_loopdev(loopcxt_get_device(&lc))) ++ loopcxt_add_device(&lc); + do { + const char *errpre; + 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 <smoser@ubuntu.com> +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 <smoser@ubuntu.com> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + 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-sfdisk-y-n-miscmatch.patch b/SOURCES/2.24-sfdisk-y-n-miscmatch.patch new file mode 100644 index 0000000..68ef91b --- /dev/null +++ b/SOURCES/2.24-sfdisk-y-n-miscmatch.patch @@ -0,0 +1,15 @@ +diff -up util-linux-2.23.2/fdisks/sfdisk.c.kzak util-linux-2.23.2/fdisks/sfdisk.c +--- util-linux-2.23.2/fdisks/sfdisk.c.kzak 2013-07-30 10:39:26.200738180 +0200 ++++ util-linux-2.23.2/fdisks/sfdisk.c 2013-10-07 11:52:10.701445115 +0200 +@@ -3201,9 +3201,9 @@ do_fdisk(char *dev) { + ignore_result( fgets(answer, sizeof(answer), stdin) ); + if (answer[0] == 'q' || answer[0] == 'Q') { + errx(EXIT_FAILURE, _("Quitting - nothing changed")); +- } else if (rpmatch(answer) == 1) { +- continue; + } else if (rpmatch(answer) == 0) { ++ continue; ++ } else if (rpmatch(answer) == 1) { + break; + } else { + printf(_("Please answer one of y,n,q\n")); diff --git a/SOURCES/2.24-su-fix-lastlog-and-btmp-logging.patch b/SOURCES/2.24-su-fix-lastlog-and-btmp-logging.patch new file mode 100644 index 0000000..40c6081 --- /dev/null +++ b/SOURCES/2.24-su-fix-lastlog-and-btmp-logging.patch @@ -0,0 +1,48 @@ +From 9b5dc4cb8d5d82c31c0cda898832998c21afc303 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 9 Sep 2013 12:24:01 +0200 +Subject: [PATCH] su: fix lastlog and btmp logging + +The su(1) logging code mix ups "old" and "new" passwd structs. The +result is things like + + Sep 9 11:50:45 x2 su: (to kzak) kzak on none + +in /var/log/messages. The right log entry is + + Sep 9 11:50:45 x2 su: (to root) kzak on pts/3 + +The bug has been introduced by commit c74a7af17c7a176c358dfaa8e1814786c89ebc14. + +References: https://bugzilla.redhat.com/show_bug.cgi?id=1005194 +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + login-utils/su-common.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/login-utils/su-common.c b/login-utils/su-common.c +index ade5c92..858af01 100644 +--- a/login-utils/su-common.c ++++ b/login-utils/su-common.c +@@ -161,7 +161,7 @@ log_syslog(struct passwd const *pw, bool successful) + old_user = pwd ? pwd->pw_name : ""; + } + +- if (get_terminal_name(STDERR_FILENO, NULL, &tty, NULL) == 0 && tty) ++ if (get_terminal_name(STDERR_FILENO, NULL, &tty, NULL) != 0 || !tty) + tty = "none"; + + openlog (program_invocation_short_name, 0 , LOG_AUTH); +@@ -483,9 +483,6 @@ authenticate (const struct passwd *pw) + + done: + +- if (lpw && lpw->pw_name) +- pw = lpw; +- + log_syslog(pw, !is_pam_failure(retval)); + + if (is_pam_failure(retval)) +-- +1.8.1.4 + diff --git a/SOURCES/2.24-su-suppress-PAM-info-messages.patch b/SOURCES/2.24-su-suppress-PAM-info-messages.patch new file mode 100644 index 0000000..3b54521 --- /dev/null +++ b/SOURCES/2.24-su-suppress-PAM-info-messages.patch @@ -0,0 +1,48 @@ +diff -up util-linux-2.23.2/login-utils/su-common.c.kzak util-linux-2.23.2/login-utils/su-common.c +--- util-linux-2.23.2/login-utils/su-common.c.kzak 2013-07-30 10:39:26.223738407 +0200 ++++ util-linux-2.23.2/login-utils/su-common.c 2013-09-09 09:32:05.497238691 +0200 +@@ -111,6 +111,9 @@ static int same_session = 0; + /* SU_MODE_{RUNUSER,SU} */ + static int su_mode; + ++/* Don't print PAM info messages (Last login, etc.). */ ++static int suppress_pam_info; ++ + static bool _pam_session_opened; + static bool _pam_cred_established; + static sig_atomic_t volatile caught_signal = false; +@@ -208,10 +211,23 @@ static void log_btmp(struct passwd const + updwtmp(_PATH_BTMP, &ut); + } + ++ ++static int su_pam_conv(int num_msg, const struct pam_message **msg, ++ struct pam_response **resp, void *appdata_ptr) ++{ ++ if (suppress_pam_info ++ && num_msg == 1 ++ && msg ++ && msg[0]->msg_style == PAM_TEXT_INFO) ++ return PAM_SUCCESS; ++ ++ return misc_conv(num_msg, msg, resp, appdata_ptr); ++} ++ + static struct pam_conv conv = + { +- misc_conv, +- NULL ++ su_pam_conv, ++ NULL + }; + + static void +@@ -902,6 +918,9 @@ su_main (int argc, char **argv, int mode + + init_groups (pw, groups, num_supp_groups); + ++ if (!simulate_login || command) ++ suppress_pam_info = 1; /* don't print PAM info messages */ ++ + create_watching_parent (); + /* Now we're in the child. */ diff --git a/SOURCES/2.24-tests-portability.patch b/SOURCES/2.24-tests-portability.patch new file mode 100644 index 0000000..be6f0b1 --- /dev/null +++ b/SOURCES/2.24-tests-portability.patch @@ -0,0 +1,37 @@ +diff -up util-linux-2.23.2/tests/functions.sh.kzak util-linux-2.23.2/tests/functions.sh +--- util-linux-2.23.2/tests/functions.sh.kzak 2013-06-13 09:46:10.554651768 +0200 ++++ util-linux-2.23.2/tests/functions.sh 2013-09-09 10:01:23.355469268 +0200 +@@ -483,7 +483,7 @@ function ts_scsi_debug_init { + modprobe scsi_debug $* + [ "$?" == 0 ] || ts_die "Cannot init device" + +- DEVNAME=$(grep scsi_debug /sys/block/*/device/model | awk -F '/' '{print $4}') ++ DEVNAME=$(grep --with-filename scsi_debug /sys/block/*/device/model | awk -F '/' '{print $4}') + [ "x${DEVNAME}" == "x" ] && ts_die "Cannot find device" + + DEVICE="/dev/${DEVNAME}" +diff -up util-linux-2.23.2/tests/ts/cramfs/mkfs.kzak util-linux-2.23.2/tests/ts/cramfs/mkfs +--- util-linux-2.23.2/tests/ts/cramfs/mkfs.kzak 2013-06-13 09:46:10.557651793 +0200 ++++ util-linux-2.23.2/tests/ts/cramfs/mkfs 2013-09-09 10:01:23.355469268 +0200 +@@ -80,7 +80,7 @@ cd $TS_MOUNTPOINT + + ts_log "list the image" + export TZ='GMT-1' +-ls -laR --time-style=long-iso . >> $TS_OUTPUT ++ls -laR --time-style=long-iso . | sed 's:\. : :g' >> $TS_OUTPUT + echo >> $TS_OUTPUT + + ts_log "list checksums from new data" +diff -up util-linux-2.23.2/tests/ts/libmount/context-utab.kzak util-linux-2.23.2/tests/ts/libmount/context-utab +--- util-linux-2.23.2/tests/ts/libmount/context-utab.kzak 2013-06-13 09:46:10.561651827 +0200 ++++ util-linux-2.23.2/tests/ts/libmount/context-utab 2013-09-09 10:01:23.355469268 +0200 +@@ -85,7 +85,9 @@ grep -q $DEVICE $LIBMOUNT_UTAB && \ + echo "umount (mountpoint) failed: found $DEVICE in $LIBMOUNT_UTAB" >> $TS_OUTPUT 2>&1 + ts_finalize_subtest + ++ + if [ -x "/sbin/mkfs.btrfs" ]; then ++ $TS_CMD_WIPEFS -a $DEVICE &> /dev/null + ts_log "Create filesystem [btrfs]" + /sbin/mkfs.btrfs -L "$LABEL" $DEVICE &> /dev/null + udevadm settle 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 <stdio.h> + #include <stdlib.h> + #include <unistd.h> ++#include <sys/wait.h> ++#include <sys/mount.h> ++ ++/* we only need some defines missing in sys/mount.h, no libmount linkage */ ++#include <libmount.h> + + #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] <program> [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 <program>\n"), out); ++ fputs(_(" --mount-proc[=<dir>] 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.24-utmpdump-ipv6.patch b/SOURCES/2.24-utmpdump-ipv6.patch new file mode 100644 index 0000000..562892a --- /dev/null +++ b/SOURCES/2.24-utmpdump-ipv6.patch @@ -0,0 +1,56 @@ +diff -up util-linux-2.23.2/login-utils/utmpdump.c.kzak util-linux-2.23.2/login-utils/utmpdump.c +--- util-linux-2.23.2/login-utils/utmpdump.c.kzak 2013-10-07 11:56:02.030191040 +0200 ++++ util-linux-2.23.2/login-utils/utmpdump.c 2013-10-07 12:05:08.671171091 +0200 +@@ -85,11 +85,14 @@ static void xcleanse(char *s, int len) + + static void print_utline(struct utmp ut) + { +- char *addr_string, *time_string; +- struct in_addr in; ++ const char *addr_string, *time_string; ++ char buffer[INET6_ADDRSTRLEN]; ++ ++ if (ut.ut_addr_v6[1] || ut.ut_addr_v6[2] || ut.ut_addr_v6[3]) ++ addr_string = inet_ntop(AF_INET6, &(ut.ut_addr_v6), buffer, sizeof(buffer)); ++ else ++ addr_string = inet_ntop(AF_INET, &(ut.ut_addr_v6), buffer, sizeof(buffer)); + +- in.s_addr = ut.ut_addr; +- addr_string = inet_ntoa(in); + time_string = timetostr(ut.ut_time); + cleanse(ut.ut_id); + cleanse(ut.ut_user); +@@ -97,7 +100,7 @@ static void print_utline(struct utmp ut) + cleanse(ut.ut_host); + + /* pid id user line host addr time */ +- printf("[%d] [%05d] [%-4.4s] [%-*.*s] [%-*.*s] [%-*.*s] [%-15.15s] [%-28.28s]\n", ++ printf("[%d] [%05d] [%-4.4s] [%-*.*s] [%-*.*s] [%-*.*s] [%-15s] [%-28.28s]\n", + ut.ut_type, ut.ut_pid, ut.ut_id, 8, UT_NAMESIZE, ut.ut_user, + 12, UT_LINESIZE, ut.ut_line, 20, UT_HOSTSIZE, ut.ut_host, + addr_string, time_string); +@@ -248,11 +251,10 @@ static int gettok(char *line, char *dest + static void undump(FILE *fp) + { + struct utmp ut; +- char s_addr[16], s_time[29], *linestart, *line; ++ char s_addr[INET6_ADDRSTRLEN + 1], s_time[29], *linestart, *line; + int count = 0; + + line = linestart = xmalloc(1024 * sizeof(*linestart)); +- s_addr[15] = 0; + s_time[28] = 0; + + while (fgets(linestart, 1023, fp)) { +@@ -267,7 +269,10 @@ static void undump(FILE *fp) + line += gettok(line, s_addr, sizeof(s_addr) - 1, 1); + line += gettok(line, s_time, sizeof(s_time) - 1, 0); + +- ut.ut_addr = inet_addr(s_addr); ++ if (strchr(s_addr, '.')) ++ inet_pton(AF_INET, s_addr, &(ut.ut_addr_v6)); ++ else ++ inet_pton(AF_INET6, s_addr, &(ut.ut_addr_v6)); + ut.ut_time = strtotime(s_time); + + ignore_result( fwrite(&ut, sizeof(ut), 1, stdout) ); diff --git a/SOURCES/2.25-blockdev-geom.patch b/SOURCES/2.25-blockdev-geom.patch new file mode 100644 index 0000000..4bf6b61 --- /dev/null +++ b/SOURCES/2.25-blockdev-geom.patch @@ -0,0 +1,95 @@ +diff -up util-linux-2.23.2/disk-utils/blockdev.c.kzak util-linux-2.23.2/disk-utils/blockdev.c +--- util-linux-2.23.2/disk-utils/blockdev.c.kzak 2015-06-23 11:29:27.818001654 +0200 ++++ util-linux-2.23.2/disk-utils/blockdev.c 2015-06-23 11:29:43.752884860 +0200 +@@ -16,6 +16,7 @@ + #include "blkdev.h" + #include "pathnames.h" + #include "closestream.h" ++#include "sysfs.h" + + struct bdc { + long ioc; /* ioctl code */ +@@ -364,7 +365,7 @@ static void do_commands(int fd, char **a + } + + if (res == -1) { +- perror(bdcms[j].iocname); ++ warn(_("ioctl error on %s"), bdcms[j].iocname); + if (verbose) + printf(_("%s failed.\n"), _(bdcms[j].help)); + exit(EXIT_FAILURE); +@@ -436,7 +437,8 @@ static void report_device(char *device, + int ro, ssz, bsz; + long ra; + unsigned long long bytes; +- struct hd_geometry g; ++ uint64_t start = 0; ++ struct stat st; + + fd = open(device, O_RDONLY | O_NONBLOCK); + if (fd < 0) { +@@ -446,15 +448,27 @@ static void report_device(char *device, + } + + ro = ssz = bsz = 0; +- g.start = ra = 0; ++ ra = 0; ++ if (fstat(fd, &st) == 0 && !sysfs_devno_is_wholedisk(st.st_rdev)) { ++ struct sysfs_cxt cxt; ++ ++ if (sysfs_init(&cxt, st.st_rdev, NULL)) ++ err(EXIT_FAILURE, ++ _("%s: failed to initialize sysfs handler"), ++ device); ++ if (sysfs_read_u64(&cxt, "start", &start)) ++ err(EXIT_FAILURE, ++ _("%s: failed to read partition start from sysfs"), ++ device); ++ sysfs_deinit(&cxt); ++ } + if (ioctl(fd, BLKROGET, &ro) == 0 && + ioctl(fd, BLKRAGET, &ra) == 0 && + ioctl(fd, BLKSSZGET, &ssz) == 0 && + ioctl(fd, BLKBSZGET, &bsz) == 0 && +- ioctl(fd, HDIO_GETGEO, &g) == 0 && + blkdev_get_size(fd, &bytes) == 0) { +- printf("%s %5ld %5d %5d %10ld %15lld %s\n", +- ro ? "ro" : "rw", ra, ssz, bsz, g.start, bytes, device); ++ printf("%s %5ld %5d %5d %10ju %15lld %s\n", ++ ro ? "ro" : "rw", ra, ssz, bsz, start, bytes, device); + } else { + if (!quiet) + warnx(_("ioctl error on %s"), device); +diff -up util-linux-2.23.2/include/sysfs.h.kzak util-linux-2.23.2/include/sysfs.h +--- util-linux-2.23.2/include/sysfs.h.kzak 2015-06-23 11:32:12.709793086 +0200 ++++ util-linux-2.23.2/include/sysfs.h 2015-06-23 11:32:31.909652361 +0200 +@@ -72,6 +72,7 @@ extern int sysfs_is_partition_dirent(DIR + + extern int sysfs_devno_to_wholedisk(dev_t dev, char *diskname, + size_t len, dev_t *diskdevno); ++extern int sysfs_devno_is_wholedisk(dev_t devno); + + extern int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, + int *c, int *t, int *l); +diff -up util-linux-2.23.2/lib/sysfs.c.kzak util-linux-2.23.2/lib/sysfs.c +--- util-linux-2.23.2/lib/sysfs.c.kzak 2015-06-23 11:31:32.166090250 +0200 ++++ util-linux-2.23.2/lib/sysfs.c 2015-06-23 11:31:59.684888551 +0200 +@@ -638,6 +638,18 @@ err: + return -1; + } + ++/* ++ * Return 0 or 1, or < 0 in case of error ++ */ ++int sysfs_devno_is_wholedisk(dev_t devno) ++{ ++ dev_t disk; ++ ++ if (sysfs_devno_to_wholedisk(devno, NULL, 0, &disk) != 0) ++ return -1; ++ ++ return devno == disk; ++} + + int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, int *c, int *t, int *l) + { 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-flock-nfs4.patch b/SOURCES/2.25-flock-nfs4.patch new file mode 100644 index 0000000..7a01332 --- /dev/null +++ b/SOURCES/2.25-flock-nfs4.patch @@ -0,0 +1,11 @@ +diff -up util-linux-2.23.2/sys-utils/flock.c.kzak util-linux-2.23.2/sys-utils/flock.c +--- util-linux-2.23.2/sys-utils/flock.c.kzak 2014-03-25 12:00:53.735361387 +0100 ++++ util-linux-2.23.2/sys-utils/flock.c 2014-03-25 12:01:44.534886083 +0100 +@@ -250,6 +250,7 @@ int main(int argc, char *argv[]) + /* otherwise try again */ + continue; + case EIO: ++ case EBADF: /* since Linux 3.4 (commit 55725513) */ + /* Probably NFSv4 where flock() is emulated by fcntl(). + * Let's try to reopen in read-write mode. + */ diff --git a/SOURCES/2.25-fsck-nohelper.patch b/SOURCES/2.25-fsck-nohelper.patch new file mode 100644 index 0000000..b8dc8fa --- /dev/null +++ b/SOURCES/2.25-fsck-nohelper.patch @@ -0,0 +1,173 @@ +diff -up util-linux-2.23.2/disk-utils/fsck.c.kzak util-linux-2.23.2/disk-utils/fsck.c +--- util-linux-2.23.2/disk-utils/fsck.c.kzak 2014-03-25 12:52:33.429389852 +0100 ++++ util-linux-2.23.2/disk-utils/fsck.c 2014-03-25 12:56:27.126804792 +0100 +@@ -79,9 +79,7 @@ static const char *really_wanted[] = { + "ext4dev", + "jfs", + "reiserfs", +- "xiafs", +- "xfs", +- NULL ++ "xiafs" + }; + + /* +@@ -167,6 +165,19 @@ static int string_to_int(const char *s) + return (int) l; + } + ++/* Do we really really want to check this fs? */ ++static int fs_check_required(const char *type) ++{ ++ size_t i; ++ ++ for(i = 0; i < ARRAY_SIZE(really_wanted); i++) { ++ if (strcmp(type, really_wanted[i]) == 0) ++ return 1; ++ } ++ ++ return 0; ++} ++ + static int is_mounted(struct libmnt_fs *fs) + { + int rc; +@@ -546,17 +557,17 @@ static void print_stats(struct fsck_inst + * Execute a particular fsck program, and link it into the list of + * child processes we are waiting for. + */ +-static int execute(const char *type, struct libmnt_fs *fs, int interactive) ++static int execute(const char *progname, const char *progpath, ++ const char *type, struct libmnt_fs *fs, int interactive) + { +- char *s, *argv[80], prog[80]; ++ char *argv[80]; + int argc, i; + struct fsck_instance *inst, *p; + pid_t pid; + + inst = xcalloc(1, sizeof(*inst)); + +- sprintf(prog, "fsck.%s", type); +- argv[0] = xstrdup(prog); ++ argv[0] = xstrdup(progname); + argc = 1; + + for (i=0; i <num_args; i++) +@@ -583,19 +594,12 @@ static int execute(const char *type, str + argv[argc++] = xstrdup(fs_get_device(fs)); + argv[argc] = 0; + +- s = find_fsck(prog); +- if (s == NULL) { +- warnx(_("%s: not found"), prog); +- free(inst); +- return ENOENT; +- } +- + if (verbose || noexecute) { + const char *tgt = mnt_fs_get_target(fs); + + if (!tgt) + tgt = fs_get_device(fs); +- printf("[%s (%d) -- %s] ", s, num_running, tgt); ++ printf("[%s (%d) -- %s] ", progpath, num_running, tgt); + for (i=0; i < argc; i++) + printf("%s ", argv[i]); + printf("\n"); +@@ -617,15 +621,15 @@ static int execute(const char *type, str + } else if (pid == 0) { + if (!interactive) + close(0); +- execv(s, argv); +- err(FSCK_EX_ERROR, _("%s: execute failed"), s); ++ execv(progpath, argv); ++ err(FSCK_EX_ERROR, _("%s: execute failed"), progpath); + } + + for (i=0; i < argc; i++) + free(argv[i]); + + inst->pid = pid; +- inst->prog = xstrdup(prog); ++ inst->prog = xstrdup(progname); + inst->type = xstrdup(type); + gettimeofday(&inst->start_time, NULL); + inst->next = NULL; +@@ -822,6 +826,7 @@ static int wait_many(int flags) + */ + static int fsck_device(struct libmnt_fs *fs, int interactive) + { ++ char progname[80], *progpath; + const char *type; + int retval; + +@@ -838,15 +843,27 @@ static int fsck_device(struct libmnt_fs + else + type = DEFAULT_FSTYPE; + ++ sprintf(progname, "fsck.%s", type); ++ progpath = find_fsck(progname); ++ if (progpath == NULL) { ++ if (fs_check_required(type)) { ++ retval = ENOENT; ++ goto err; ++ } ++ return 0; ++ } ++ + num_running++; +- retval = execute(type, fs, interactive); ++ retval = execute(progname, progpath, type, fs, interactive); + if (retval) { +- warnx(_("error %d while executing fsck.%s for %s"), +- retval, type, fs_get_device(fs)); + num_running--; +- return FSCK_EX_ERROR; ++ goto err; + } + return 0; ++err: ++ warnx(_("error %d (%m) while executing fsck.%s for %s"), ++ retval, type, fs_get_device(fs)); ++ return FSCK_EX_ERROR; + } + + +@@ -1014,8 +1031,7 @@ static int fs_ignored_type(struct libmnt + /* Check if we should ignore this filesystem. */ + static int ignore(struct libmnt_fs *fs) + { +- const char **ip, *type; +- int wanted = 0; ++ const char *type; + + /* + * If the pass number is 0, ignore it. +@@ -1070,16 +1086,11 @@ static int ignore(struct libmnt_fs *fs) + if (fs_ignored_type(fs)) + return 1; + +- /* Do we really really want to check this fs? */ +- for(ip = really_wanted; *ip; ip++) +- if (strcmp(type, *ip) == 0) { +- wanted = 1; +- break; +- } ++ + + /* See if the <fsck.fs> program is available. */ + if (find_fsck(type) == NULL) { +- if (wanted) ++ if (fs_check_required(type)) + warnx(_("cannot check %s: fsck.%s not found"), + fs_get_device(fs), type); + return 1; +@@ -1557,7 +1568,6 @@ int main(int argc, char *argv[]) + fs = add_dummy_fs(devices[i]); + else if (fs_ignored_type(fs)) + continue; +- + if (ignore_mounted && is_mounted(fs)) + continue; + status |= fsck_device(fs, interactive); diff --git a/SOURCES/2.25-fstrim-all.patch b/SOURCES/2.25-fstrim-all.patch new file mode 100644 index 0000000..233e2ec --- /dev/null +++ b/SOURCES/2.25-fstrim-all.patch @@ -0,0 +1,496 @@ +diff -up util-linux-2.23.2/libmount/src/libmount.h.in.kzak util-linux-2.23.2/libmount/src/libmount.h.in +--- util-linux-2.23.2/libmount/src/libmount.h.in.kzak 2015-06-23 11:54:00.510210784 +0200 ++++ util-linux-2.23.2/libmount/src/libmount.h.in 2015-06-23 11:54:58.592785482 +0200 +@@ -420,6 +420,15 @@ extern int mnt_table_get_root_fs(struct + extern int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr, + struct libmnt_fs *fs); + ++enum { ++ MNT_UNIQ_FORWARD = (1 << 1), /* default is backward */ ++ MNT_UNIQ_KEEPTREE = (1 << 2) ++}; ++extern int mnt_table_uniq_fs(struct libmnt_table *tb, int flags, ++ int (*cmp)(struct libmnt_table *, ++ struct libmnt_fs *, ++ struct libmnt_fs *)); ++ + extern struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb, + const char *path, int direction); + extern struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, +diff -up util-linux-2.23.2/libmount/src/libmount.sym.kzak util-linux-2.23.2/libmount/src/libmount.sym +--- util-linux-2.23.2/libmount/src/libmount.sym.kzak 2015-06-23 11:56:47.259989779 +0200 ++++ util-linux-2.23.2/libmount/src/libmount.sym 2015-06-23 11:56:17.681206366 +0200 +@@ -256,3 +256,9 @@ global: + mnt_context_find_umount_fs; + mnt_table_find_mountpoint; + } MOUNT_2.22; ++ ++/* backport from v2.25 to RHEL7 */ ++MOUNT_2.25 { ++ mnt_table_uniq_fs; ++} MOUNT_2.23; ++ +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 2015-06-23 11:52:04.750058424 +0200 ++++ util-linux-2.23.2/libmount/src/tab.c 2015-06-23 11:53:26.109462680 +0200 +@@ -398,6 +398,93 @@ int mnt_table_find_next_fs(struct libmnt + return 1; + } + ++static int mnt_table_move_parent(struct libmnt_table *tb, int oldid, int newid) ++{ ++ struct libmnt_iter itr; ++ struct libmnt_fs *fs; ++ ++ if (!tb) ++ return -EINVAL; ++ if (list_empty(&tb->ents)) ++ return 0; ++ ++ ++ mnt_reset_iter(&itr, MNT_ITER_FORWARD); ++ ++ while (mnt_table_next_fs(tb, &itr, &fs) == 0) { ++ if (fs->parent == oldid) ++ fs->parent = newid; ++ } ++ return 0; ++} ++ ++/** ++ * mnt_table_uniq_fs: ++ * @tb: table ++ * @flags: MNT_UNIQ_* ++ * @cmp: function to compare filesystems ++ * ++ * This function de-duplicate the @tb, but does not change order of the ++ * filesystems. The @cmp function has to return 0 if the filesystems are ++ * equal, otherwise non-zero. ++ * ++ * The default is to keep in the table later mounted filesystems (function uses ++ * backward mode iterator). ++ * ++ * @MNT_UNIQ_FORWARD: remove later mounted filesystems ++ * @MNT_UNIQ_KEEPTREE: keep parent->id relation ship stil valid ++ * ++ * Returns: negative number in case of error, or 0 o success. ++ */ ++int mnt_table_uniq_fs(struct libmnt_table *tb, int flags, ++ int (*cmp)(struct libmnt_table *, ++ struct libmnt_fs *, ++ struct libmnt_fs *)) ++{ ++ struct libmnt_iter itr; ++ struct libmnt_fs *fs; ++ int direction = MNT_ITER_BACKWARD; ++ ++ if (!tb || !cmp) ++ return -EINVAL; ++ if (list_empty(&tb->ents)) ++ return 0; ++ ++ if (flags & MNT_UNIQ_FORWARD) ++ direction = MNT_ITER_FORWARD; ++ ++ ++ mnt_reset_iter(&itr, direction); ++ ++ if ((flags & MNT_UNIQ_KEEPTREE) && !is_mountinfo(tb)) ++ flags &= ~MNT_UNIQ_KEEPTREE; ++ ++ while (mnt_table_next_fs(tb, &itr, &fs) == 0) { ++ int want = 1; ++ struct libmnt_iter xtr; ++ struct libmnt_fs *x; ++ ++ mnt_reset_iter(&xtr, direction); ++ while (want && mnt_table_next_fs(tb, &xtr, &x) == 0) { ++ if (fs == x) ++ break; ++ want = cmp(tb, x, fs) != 0; ++ } ++ ++ if (!want) { ++ if (flags & MNT_UNIQ_KEEPTREE) ++ mnt_table_move_parent(tb, mnt_fs_get_id(fs), ++ mnt_fs_get_parent_id(fs)); ++ ++ ++ ++ mnt_table_remove_fs(tb, fs); ++ } ++ } ++ ++ return 0; ++} ++ + /** + * mnt_table_set_iter: + * @tb: tab pointer +diff -up util-linux-2.23.2/sys-utils/fstrim.c.kzak util-linux-2.23.2/sys-utils/fstrim.c +--- util-linux-2.23.2/sys-utils/fstrim.c.kzak 2013-06-13 09:46:10.535651605 +0200 ++++ util-linux-2.23.2/sys-utils/fstrim.c 2015-06-23 11:57:59.435461283 +0200 +@@ -41,6 +41,11 @@ + #include "strutils.h" + #include "c.h" + #include "closestream.h" ++#include "pathnames.h" ++#include "sysfs.h" ++#include "exitcodes.h" ++ ++#include <libmount.h> + + #ifndef FITRIM + struct fstrim_range { +@@ -51,16 +56,216 @@ struct fstrim_range { + #define FITRIM _IOWR('X', 121, struct fstrim_range) + #endif + ++/* returns: 0 = success, 1 = unsupported, < 0 = error */ ++static int fstrim_filesystem(const char *path, struct fstrim_range *rangetpl, ++ int verbose) ++{ ++ int fd; ++ struct stat sb; ++ struct fstrim_range range; ++ ++ /* kernel modifies the range */ ++ memcpy(&range, rangetpl, sizeof(range)); ++ ++ fd = open(path, O_RDONLY); ++ if (fd < 0) { ++ warn(_("cannot open %s"), path); ++ return -1; ++ } ++ if (fstat(fd, &sb) == -1) { ++ warn(_("stat of %s failed"), path); ++ return -1; ++ } ++ if (!S_ISDIR(sb.st_mode)) { ++ warnx(_("%s: not a directory"), path); ++ return -1; ++ } ++ errno = 0; ++ if (ioctl(fd, FITRIM, &range)) { ++ int rc = errno == EOPNOTSUPP || errno == ENOTTY ? 1 : -1; ++ ++ if (rc != 1) ++ warn(_("%s: FITRIM ioctl failed"), path); ++ close(fd); ++ return rc; ++ } ++ ++ if (verbose) { ++ char *str = size_to_human_string( ++ SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE, ++ (uint64_t) range.len); ++ /* TRANSLATORS: The standard value here is a very large number. */ ++ printf(_("%s: %s (%" PRIu64 " bytes) trimmed\n"), ++ path, str, (uint64_t) range.len); ++ free(str); ++ } ++ close(fd); ++ return 0; ++} ++ ++static int has_discard(const char *devname, struct sysfs_cxt *wholedisk) ++{ ++ struct sysfs_cxt cxt, *parent = NULL; ++ uint64_t dg = 0; ++ dev_t disk = 0, dev; ++ int rc; ++ ++ dev = sysfs_devname_to_devno(devname, NULL); ++ if (!dev) ++ return 1; ++ /* ++ * This is tricky to read the info from sys/, because the queue ++ * atrributes are provided for whole devices (disk) only. We're trying ++ * to reuse the whole-disk sysfs context to optimize this stuff (as ++ * system usually have just one disk only). ++ */ ++ if (sysfs_devno_to_wholedisk(dev, NULL, 0, &disk) || !disk) ++ return 1; ++ if (dev != disk) { ++ if (wholedisk->devno != disk) { ++ sysfs_deinit(wholedisk); ++ if (sysfs_init(wholedisk, disk, NULL)) ++ return 1; ++ } ++ parent = wholedisk; ++ } ++ ++ rc = sysfs_init(&cxt, dev, parent); ++ if (!rc) ++ rc = sysfs_read_u64(&cxt, "queue/discard_granularity", &dg); ++ ++ sysfs_deinit(&cxt); ++ return rc == 0 && dg > 0; ++} ++ ++ ++static int uniq_fs_target_cmp( ++ struct libmnt_table *tb __attribute__((__unused__)), ++ struct libmnt_fs *a, ++ struct libmnt_fs *b) ++{ ++ return !mnt_fs_streq_target(a, mnt_fs_get_target(b)); ++} ++ ++static int uniq_fs_source_cmp( ++ struct libmnt_table *tb __attribute__((__unused__)), ++ struct libmnt_fs *a, ++ struct libmnt_fs *b) ++{ ++ int eq; ++ ++ if (mnt_fs_is_pseudofs(a) || mnt_fs_is_netfs(a) || ++ mnt_fs_is_pseudofs(b) || mnt_fs_is_netfs(b)) ++ return 1; ++ ++ eq = mnt_fs_streq_srcpath(a, mnt_fs_get_srcpath(b)); ++ if (eq) { ++ const char *aroot = mnt_fs_get_root(a), ++ *broot = mnt_fs_get_root(b); ++ if (!aroot || !broot) ++ eq = 0; ++ else if (strcmp(aroot, broot) != 0) ++ eq = 0; ++ } ++ ++ return !eq; ++} ++ ++/* ++ * fstrim --all follows "mount -a" return codes: ++ * ++ * 0 = all success ++ * 32 = all failed ++ * 64 = some failed, some success ++ */ ++static int fstrim_all(struct fstrim_range *rangetpl, int verbose) ++{ ++ struct libmnt_fs *fs; ++ struct libmnt_iter *itr; ++ struct libmnt_table *tab; ++ struct sysfs_cxt wholedisk = UL_SYSFSCXT_EMPTY; ++ int cnt = 0, cnt_err = 0; ++ ++ mnt_init_debug(0); ++ ++ itr = mnt_new_iter(MNT_ITER_BACKWARD); ++ if (!itr) ++ err(MOUNT_EX_FAIL, _("failed to initialize libmount iterator")); ++ ++ tab = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); ++ if (!tab) ++ err(MOUNT_EX_FAIL, _("failed to parse %s"), _PATH_PROC_MOUNTINFO); ++ ++ /* de-duplicate by mountpoints */ ++ mnt_table_uniq_fs(tab, 0, uniq_fs_target_cmp); ++ ++ /* de-duplicate by source and root */ ++ mnt_table_uniq_fs(tab, 0, uniq_fs_source_cmp); ++ ++ while (mnt_table_next_fs(tab, itr, &fs) == 0) { ++ const char *src = mnt_fs_get_srcpath(fs), ++ *tgt = mnt_fs_get_target(fs); ++ char *path; ++ int rc = 1; ++ ++ if (!src || !tgt || *src != '/' || ++ mnt_fs_is_pseudofs(fs) || ++ mnt_fs_is_netfs(fs)) ++ continue; ++ ++ /* Is it really accessible mountpoint? Not all mountpoints are ++ * accessible (maybe over mounted by another fylesystem) */ ++ path = mnt_get_mountpoint(tgt); ++ if (path && strcmp(path, tgt) == 0) ++ rc = 0; ++ free(path); ++ if (rc) ++ continue; /* overlaying mount */ ++ ++ if (!has_discard(src, &wholedisk)) ++ continue; ++ cnt++; ++ ++ /* ++ * We're able to detect that the device supports discard, but ++ * things also depend on filesystem or device mapping, for ++ * example vfat or LUKS (by default) does not support FSTRIM. ++ * ++ * This is reason why we ignore EOPNOTSUPP and ENOTTY errors ++ * from discard ioctl. ++ */ ++ if (fstrim_filesystem(tgt, rangetpl, verbose) < 0) ++ cnt_err++; ++ } ++ ++ sysfs_deinit(&wholedisk); ++ mnt_free_table(tab); ++ mnt_free_iter(itr); ++ ++ if (cnt && cnt == cnt_err) ++ return MOUNT_EX_FAIL; /* all failed */ ++ if (cnt && cnt_err) ++ return MOUNT_EX_SOMEOK; /* some ok */ ++ ++ return EXIT_SUCCESS; ++} ++ + static void __attribute__((__noreturn__)) usage(FILE *out) + { + fputs(USAGE_HEADER, out); + fprintf(out, + _(" %s [options] <mount point>\n"), program_invocation_short_name); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Discard unused blocks on a mounted filesystem.\n"), out); ++ + fputs(USAGE_OPTIONS, out); +- fputs(_(" -o, --offset <num> offset in bytes to discard from\n" +- " -l, --length <num> length of bytes to discard from the offset\n" +- " -m, --minimum <num> minimum extent length to discard\n" +- " -v, --verbose print number of discarded bytes\n"), out); ++ fputs(_(" -a, --all trim all mounted filesystems that are supported\n"), out); ++ fputs(_(" -o, --offset <num> the offset in bytes to start discarding from\n"), out); ++ fputs(_(" -l, --length <num> the number of bytes to discard\n"), out); ++ fputs(_(" -m, --minimum <num> the minimum extent length to discard\n"), out); ++ fputs(_(" -v, --verbose print number of discarded bytes\n"), out); ++ + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); + fputs(USAGE_VERSION, out); +@@ -70,12 +275,12 @@ static void __attribute__((__noreturn__) + + int main(int argc, char **argv) + { +- char *path; +- int c, fd, verbose = 0; ++ char *path = NULL; ++ int c, rc, verbose = 0, all = 0; + struct fstrim_range range; +- struct stat sb; + + static const struct option longopts[] = { ++ { "all", 0, 0, 'a' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "offset", 1, 0, 'o' }, +@@ -93,8 +298,11 @@ int main(int argc, char **argv) + memset(&range, 0, sizeof(range)); + range.len = ULLONG_MAX; + +- while ((c = getopt_long(argc, argv, "hVo:l:m:v", longopts, NULL)) != -1) { ++ while ((c = getopt_long(argc, argv, "ahVo:l:m:v", longopts, NULL)) != -1) { + switch(c) { ++ case 'a': ++ all = 1; ++ break; + case 'h': + usage(stdout); + break; +@@ -122,38 +330,26 @@ int main(int argc, char **argv) + } + } + +- if (optind == argc) +- errx(EXIT_FAILURE, _("no mountpoint specified")); +- +- path = argv[optind++]; ++ if (!all) { ++ if (optind == argc) ++ errx(EXIT_FAILURE, _("no mountpoint specified")); ++ path = argv[optind++]; ++ } + + if (optind != argc) { + warnx(_("unexpected number of arguments")); + usage(stderr); + } + +- if (stat(path, &sb) == -1) +- err(EXIT_FAILURE, _("stat failed %s"), path); +- if (!S_ISDIR(sb.st_mode)) +- errx(EXIT_FAILURE, _("%s: not a directory"), path); +- +- fd = open(path, O_RDONLY); +- if (fd < 0) +- err(EXIT_FAILURE, _("cannot open %s"), path); +- +- if (ioctl(fd, FITRIM, &range)) +- err(EXIT_FAILURE, _("%s: FITRIM ioctl failed"), path); +- +- if (verbose) { +- char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | +- SIZE_SUFFIX_SPACE, +- (uint64_t) range.len); +- /* TRANSLATORS: The standard value here is a very large number. */ +- printf(_("%s: %s (%" PRIu64 " bytes) trimmed\n"), +- path, str, +- (uint64_t) range.len); +- free(str); ++ if (all) ++ rc = fstrim_all(&range, verbose); ++ else { ++ rc = fstrim_filesystem(path, &range, verbose); ++ if (rc == 1) { ++ warnx(_("%s: the discard operation is not supported"), path); ++ rc = EXIT_FAILURE; ++ } + } +- close(fd); +- return EXIT_SUCCESS; ++ ++ return rc; + } +diff -up util-linux-2.23.2/sys-utils/fstrim.service.in.kzak util-linux-2.23.2/sys-utils/fstrim.service +--- util-linux-2.23.2/sys-utils/fstrim.service.in.kzak 2015-06-23 12:02:18.505564273 +0200 ++++ util-linux-2.23.2/sys-utils/fstrim.service.in 2015-06-23 12:02:05.049662802 +0200 +@@ -0,0 +1,6 @@ ++[Unit] ++Description=Discard unused blocks ++ ++[Service] ++Type=oneshot ++ExecStart=@sbindir@/fstrim -a +diff -up util-linux-2.23.2/sys-utils/fstrim.timer.kzak util-linux-2.23.2/sys-utils/fstrim.timer +--- util-linux-2.23.2/sys-utils/fstrim.timer.kzak 2015-06-23 12:02:18.505564273 +0200 ++++ util-linux-2.23.2/sys-utils/fstrim.timer 2015-06-23 12:02:05.049662802 +0200 +@@ -0,0 +1,11 @@ ++[Unit] ++Description=Discard unused blocks once a week ++Documentation=man:fstrim ++ ++[Timer] ++OnCalendar=weekly ++AccuracySec=1h ++Persistent=true ++ ++[Install] ++WantedBy=multi-user.target +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 2015-06-23 11:59:05.803975307 +0200 ++++ util-linux-2.23.2/sys-utils/Makemodule.am 2015-06-23 12:01:18.682002323 +0200 +@@ -68,7 +68,18 @@ fsfreeze_SOURCES = sys-utils/fsfreeze.c + sbin_PROGRAMS += fstrim + dist_man_MANS += sys-utils/fstrim.8 + fstrim_SOURCES = sys-utils/fstrim.c +-fstrim_LDADD = $(LDADD) libcommon.la ++fstrim_LDADD = $(LDADD) libcommon.la libmount.la ++fstrim_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir) ++ ++if HAVE_SYSTEMD ++systemdsystemunit_DATA += \ ++ sys-utils/fstrim.service \ ++ sys-utils/fstrim.timer ++endif ++ ++PATHFILES += sys-utils/fstrim.service ++EXTRA_DIST += sys-utils/fstrim.timer ++ + + sbin_PROGRAMS += blkdiscard + dist_man_MANS += sys-utils/blkdiscard.8 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 <chrismacgregor@google.com> +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 <chrismacgregor@google.com> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + 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-lib-add-path_strdup.patch b/SOURCES/2.25-lib-add-path_strdup.patch new file mode 100644 index 0000000..e3a9cc3 --- /dev/null +++ b/SOURCES/2.25-lib-add-path_strdup.patch @@ -0,0 +1,51 @@ +From dd3bc51a539ffdd5c6c6b7d0b20acd1f61bdd337 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Mon, 6 Jan 2014 16:48:13 +0100 +Subject: [PATCH] lib/path: add path_strdup() + +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + include/path.h | 2 ++ + lib/path.c | 13 +++++++++++++ + 2 files changed, 15 insertions(+) + +diff --git a/include/path.h b/include/path.h +index 615d284..45da692 100644 +--- a/include/path.h ++++ b/include/path.h +@@ -4,6 +4,8 @@ + #include <stdio.h> + #include <stdint.h> + ++extern char *path_strdup(const char *path, ...) ++ __attribute__ ((__format__ (__printf__, 1, 2))); + extern FILE *path_fopen(const char *mode, int exit_on_err, const char *path, ...) + __attribute__ ((__format__ (__printf__, 3, 4))); + extern void path_read_str(char *result, size_t len, const char *path, ...) +diff --git a/lib/path.c b/lib/path.c +index 1f7e258..42d321c 100644 +--- a/lib/path.c ++++ b/lib/path.c +@@ -49,6 +49,19 @@ path_vcreate(const char *path, va_list ap) + return pathbuf; + } + ++char * ++path_strdup(const char *path, ...) ++{ ++ const char *p; ++ va_list ap; ++ ++ va_start(ap, path); ++ p = path_vcreate(path, ap); ++ va_end(ap); ++ ++ return p ? strdup(p) : NULL; ++} ++ + static FILE * + path_vfopen(const char *mode, int exit_on_error, const char *path, va_list ap) + { +-- +1.8.4.2 + diff --git a/SOURCES/2.25-libblkid-Identify-extN-file-system-properly.patch b/SOURCES/2.25-libblkid-Identify-extN-file-system-properly.patch new file mode 100644 index 0000000..da4bcb3 --- /dev/null +++ b/SOURCES/2.25-libblkid-Identify-extN-file-system-properly.patch @@ -0,0 +1,240 @@ +diff -up util-linux-2.23.2/libblkid/src/superblocks/ext.c.kzak util-linux-2.23.2/libblkid/src/superblocks/ext.c +--- util-linux-2.23.2/libblkid/src/superblocks/ext.c.kzak 2013-06-13 09:46:10.422650639 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/ext.c 2014-01-23 10:28:51.175358545 +0100 +@@ -18,7 +18,6 @@ + #endif + #include <time.h> + +-#include "linux_version.h" + #include "superblocks.h" + + struct ext2_super_block { +@@ -132,140 +131,11 @@ struct ext2_super_block { + #define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT3_FEATURE_RO_COMPAT_SUPP + + /* +- * Check to see if a filesystem is in /proc/filesystems. +- * Returns 1 if found, 0 if not +- */ +-static int fs_proc_check(const char *fs_name) +-{ +- FILE *f; +- char buf[80], *cp, *t; +- +- f = fopen("/proc/filesystems", "r" UL_CLOEXECSTR); +- if (!f) +- return 0; +- while (!feof(f)) { +- if (!fgets(buf, sizeof(buf), f)) +- break; +- cp = buf; +- if (!isspace(*cp)) { +- while (*cp && !isspace(*cp)) +- cp++; +- } +- while (*cp && isspace(*cp)) +- cp++; +- if ((t = strchr(cp, '\n')) != NULL) +- *t = 0; +- if ((t = strchr(cp, '\t')) != NULL) +- *t = 0; +- if ((t = strchr(cp, ' ')) != NULL) +- *t = 0; +- if (!strcmp(fs_name, cp)) { +- fclose(f); +- return 1; +- } +- } +- fclose(f); +- return (0); +-} +- +-/* +- * Check to see if a filesystem is available as a module +- * Returns 1 if found, 0 if not +- */ +-static int check_for_modules(const char *fs_name) +-{ +-#ifdef __linux__ +- struct utsname uts; +- FILE *f; +- char buf[1024], *cp; +- int namesz; +- +- if (uname(&uts)) +- return 0; +- snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release); +- +- f = fopen(buf, "r" UL_CLOEXECSTR); +- if (!f) +- return 0; +- +- namesz = strlen(fs_name); +- +- while (!feof(f)) { +- if (!fgets(buf, sizeof(buf), f)) +- break; +- if ((cp = strchr(buf, ':')) != NULL) +- *cp = 0; +- else +- continue; +- if ((cp = strrchr(buf, '/')) == NULL) +- continue; +- cp++; +- +- if (!strncmp(cp, fs_name, namesz) && +- (!strcmp(cp + namesz, ".ko") || +- !strcmp(cp + namesz, ".ko.gz"))) { +- fclose(f); +- return 1; +- } +- } +- fclose(f); +-#endif /* __linux__ */ +- return 0; +-} +- +-/* + * Starting in 2.6.29, ext4 can be used to support filesystems + * without a journal. + */ + #define EXT4_SUPPORTS_EXT2 KERNEL_VERSION(2, 6, 29) + +-static int system_supports_ext2(void) +-{ +- static time_t last_check = 0; +- static int ret = -1; +- time_t now = time(0); +- +- if (ret != -1 || (now - last_check) < 5) +- return ret; +- last_check = now; +- ret = (fs_proc_check("ext2") || check_for_modules("ext2")); +- return ret; +-} +- +-static int system_supports_ext4(void) +-{ +- static time_t last_check = 0; +- static int ret = -1; +- time_t now = time(0); +- +- if (ret != -1 || (now - last_check) < 5) +- return ret; +- last_check = now; +- ret = (fs_proc_check("ext4") || check_for_modules("ext4")); +- return ret; +-} +- +-static int system_supports_ext4dev(void) +-{ +- static time_t last_check = 0; +- static int ret = -1; +- time_t now = time(0); +- +- if (ret != -1 || (now - last_check) < 5) +- return ret; +- last_check = now; +- ret = (fs_proc_check("ext4dev") || check_for_modules("ext4dev")); +- return ret; +-} +- +-static int system_supports_ext4_ext2(void) +-{ +-#ifdef __linux__ +- return get_linux_version() >= EXT4_SUPPORTS_EXT2; +-#else +- return 0; +-#endif +-} + /* + * reads superblock and returns: + * fc = feature_compat +@@ -355,15 +225,6 @@ static int probe_ext2(blkid_probe pr, + (fi & EXT2_FEATURE_INCOMPAT_UNSUPPORTED)) + return -BLKID_ERR_PARAM; + +- /* +- * If ext2 is not present, but ext4 or ext4dev are, then +- * disclaim we are ext2 +- */ +- if (!system_supports_ext2() && +- (system_supports_ext4() || system_supports_ext4dev()) && +- system_supports_ext4_ext2()) +- return -BLKID_ERR_PARAM; +- + ext_get_info(pr, 2, es); + return 0; + } +@@ -406,34 +267,9 @@ static int probe_ext4dev(blkid_probe pr, + if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + return -BLKID_ERR_PARAM; + +- /* +- * If the filesystem does not have a journal and ext2 and ext4 +- * is not present, then force this to be detected as an +- * ext4dev filesystem. +- */ +- if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && +- !system_supports_ext2() && !system_supports_ext4() && +- system_supports_ext4dev() && +- system_supports_ext4_ext2()) +- goto force_ext4dev; +- +- /* +- * If the filesystem is marked as OK for use by in-development +- * filesystem code, but ext4dev is not supported, and ext4 is, +- * then don't call ourselves ext4dev, since we should be +- * detected as ext4 in that case. +- * +- * If the filesystem is marked as in use by production +- * filesystem, then it can only be used by ext4 and NOT by +- * ext4dev, so always disclaim we are ext4dev in that case. +- */ +- if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) { +- if (!system_supports_ext4dev() && system_supports_ext4()) +- return -BLKID_ERR_PARAM; +- } else ++ if (!(le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS)) + return -BLKID_ERR_PARAM; + +-force_ext4dev: + ext_get_info(pr, 4, es); + return 0; + } +@@ -452,22 +288,11 @@ static int probe_ext4(blkid_probe pr, + if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + return -BLKID_ERR_PARAM; + +- /* +- * If the filesystem does not have a journal and ext2 is not +- * present, then force this to be detected as an ext2 +- * filesystem. +- */ +- if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && +- !system_supports_ext2() && system_supports_ext4() && +- system_supports_ext4_ext2()) +- goto force_ext4; +- + /* Ext4 has at least one feature which ext3 doesn't understand */ + if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) && + !(fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) + return -BLKID_ERR_PARAM; + +-force_ext4: + /* + * If the filesystem is a OK for use by in-development + * filesystem code, and ext4dev is supported or ext4 is not +@@ -478,10 +303,8 @@ force_ext4: + * filesystem, then it can only be used by ext4 and NOT by + * ext4dev. + */ +- if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) { +- if (system_supports_ext4dev() || !system_supports_ext4()) +- return -BLKID_ERR_PARAM; +- } ++ if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) ++ return -BLKID_ERR_PARAM; + + ext_get_info(pr, 4, es); + return 0; diff --git a/SOURCES/2.25-libblkid-detect-alone-PMBR.patch b/SOURCES/2.25-libblkid-detect-alone-PMBR.patch new file mode 100644 index 0000000..fe06167 --- /dev/null +++ b/SOURCES/2.25-libblkid-detect-alone-PMBR.patch @@ -0,0 +1,100 @@ +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 2013-07-30 10:39:26.206738239 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/gpt.c 2014-01-23 11:06:17.364011293 +0100 +@@ -156,13 +156,15 @@ static int last_lba(blkid_probe pr, uint + * Note that the PMBR detection is optional (enabled by default) and could be + * disabled by BLKID_PARTS_FOPCE_GPT flag (see also blkid_paertitions_set_flags()). + */ +-static int is_pmbr_valid(blkid_probe pr) ++static int is_pmbr_valid(blkid_probe pr, int *has) + { + int flags = blkid_partitions_get_flags(pr); + unsigned char *data; + struct dos_partition *p; + int i; + ++ if (has) ++ *has = 0; + if (flags & BLKID_PARTS_FORCE_GPT) + goto ok; /* skip PMBR check */ + +@@ -182,6 +184,8 @@ static int is_pmbr_valid(blkid_probe pr) + failed: + return 0; + ok: ++ if (has) ++ *has = 1; + return 1; + } + +@@ -305,7 +309,7 @@ static int probe_gpt_pt(blkid_probe pr, + if (last_lba(pr, &lastlba)) + goto nothing; + +- if (!is_pmbr_valid(pr)) ++ if (!is_pmbr_valid(pr, NULL)) + goto nothing; + + h = get_gpt_header(pr, &hdr, &e, (lba = GPT_PRIMARY_LBA), lastlba); +@@ -410,3 +414,39 @@ const struct blkid_idinfo gpt_pt_idinfo + .magics = BLKID_NONE_MAGIC + }; + ++ ++ ++/* probe for *alone* protective MBR */ ++static int probe_pmbr_pt(blkid_probe pr, ++ const struct blkid_idmag *mag __attribute__((__unused__))) ++{ ++ int has = 0; ++ struct gpt_entry *e; ++ uint64_t lastlba = 0; ++ struct gpt_header hdr; ++ ++ if (last_lba(pr, &lastlba)) ++ goto nothing; ++ ++ is_pmbr_valid(pr, &has); ++ if (!has) ++ goto nothing; ++ ++ if (!get_gpt_header(pr, &hdr, &e, GPT_PRIMARY_LBA, lastlba) && ++ !get_gpt_header(pr, &hdr, &e, lastlba, lastlba)) ++ return 0; ++nothing: ++ return 1; ++} ++ ++const struct blkid_idinfo pmbr_pt_idinfo = ++{ ++ .name = "PMBR", ++ .probefunc = probe_pmbr_pt, ++ .magics = ++ { ++ { .magic = "\x55\xAA", .len = 2, .sboff = 510 }, ++ { NULL } ++ } ++}; ++ +diff -up util-linux-2.23.2/libblkid/src/partitions/partitions.c.kzak util-linux-2.23.2/libblkid/src/partitions/partitions.c +--- util-linux-2.23.2/libblkid/src/partitions/partitions.c.kzak 2013-07-30 10:39:26.207738249 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/partitions.c 2014-01-23 11:06:17.364011293 +0100 +@@ -125,6 +125,7 @@ static const struct blkid_idinfo *idinfo + &sun_pt_idinfo, + &dos_pt_idinfo, + &gpt_pt_idinfo, ++ &pmbr_pt_idinfo, /* always after GPT */ + &mac_pt_idinfo, + &ultrix_pt_idinfo, + &bsd_pt_idinfo, +diff -up util-linux-2.23.2/libblkid/src/partitions/partitions.h.kzak util-linux-2.23.2/libblkid/src/partitions/partitions.h +--- util-linux-2.23.2/libblkid/src/partitions/partitions.h.kzak 2013-07-30 10:39:26.208738259 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/partitions.h 2014-01-23 11:06:17.364011293 +0100 +@@ -59,6 +59,7 @@ extern const struct blkid_idinfo mac_pt_ + extern const struct blkid_idinfo dos_pt_idinfo; + extern const struct blkid_idinfo minix_pt_idinfo; + extern const struct blkid_idinfo gpt_pt_idinfo; ++extern const struct blkid_idinfo pmbr_pt_idinfo; + extern const struct blkid_idinfo ultrix_pt_idinfo; + + #endif /* BLKID_PARTITIONS_H */ 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-io-errors.patch b/SOURCES/2.25-libblkid-io-errors.patch new file mode 100644 index 0000000..4392cf5 --- /dev/null +++ b/SOURCES/2.25-libblkid-io-errors.patch @@ -0,0 +1,2590 @@ +diff -up util-linux-2.23.2/libblkid/src/blkidP.h.kzak util-linux-2.23.2/libblkid/src/blkidP.h +--- util-linux-2.23.2/libblkid/src/blkidP.h.kzak 2014-03-28 15:11:18.334283704 +0100 ++++ util-linux-2.23.2/libblkid/src/blkidP.h 2014-03-28 15:11:44.676551975 +0100 +@@ -297,6 +297,9 @@ struct blkid_struct_cache + /* old systems */ + #define BLKID_CACHE_FILE_OLD "/etc/blkid.tab" + ++#define BLKID_PROBE_OK 0 ++#define BLKID_PROBE_NONE 1 ++ + #define BLKID_ERR_IO 5 + #define BLKID_ERR_PROC 9 + #define BLKID_ERR_MEM 12 +diff -up util-linux-2.23.2/libblkid/src/partitions/aix.c.kzak util-linux-2.23.2/libblkid/src/partitions/aix.c +--- util-linux-2.23.2/libblkid/src/partitions/aix.c.kzak 2013-05-30 15:21:43.120591308 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/aix.c 2014-03-28 15:11:44.676551975 +0100 +@@ -22,19 +22,17 @@ static int probe_aix_pt(blkid_probe pr, + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ +- return 0; ++ return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) +- goto err; ++ return BLKID_PROBE_NONE; + + tab = blkid_partlist_new_parttable(ls, "aix", 0); + if (!tab) +- goto err; ++ return -ENOMEM; + +- return 0; +-err: +- return -1; ++ return BLKID_PROBE_OK; + } + + /* +diff -up util-linux-2.23.2/libblkid/src/partitions/bsd.c.kzak util-linux-2.23.2/libblkid/src/partitions/bsd.c +--- util-linux-2.23.2/libblkid/src/partitions/bsd.c.kzak 2013-07-15 10:25:46.283049056 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/bsd.c 2014-03-28 15:11:44.677551986 +0100 +@@ -113,20 +113,24 @@ static int probe_bsd_pt(blkid_probe pr, + blkid_partlist ls; + int i, nparts = BSD_MAXPARTITIONS; + unsigned char *data; ++ int rc = BLKID_PROBE_NONE; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ +- return 0; ++ return rc; + + data = blkid_probe_get_sector(pr, BLKID_MAG_SECTOR(mag)); +- if (!data) ++ if (!data) { ++ if (errno) ++ rc = -errno; + goto nothing; ++ } + + l = (struct bsd_disklabel *) data + BLKID_MAG_LASTOFFSET(mag); + + ls = blkid_probe_get_partlist(pr); + if (!ls) +- goto err; ++ goto nothing; + + /* try to determine the real type of BSD system according to + * (parental) primary partition */ +@@ -152,8 +156,10 @@ static int probe_bsd_pt(blkid_probe pr, + } + + tab = blkid_partlist_new_parttable(ls, name, BLKID_MAG_OFFSET(mag)); +- if (!tab) +- goto err; ++ if (!tab) { ++ rc = -ENOMEM; ++ goto nothing; ++ } + + if (le16_to_cpu(l->d_npartitions) < BSD_MAXPARTITIONS) + nparts = le16_to_cpu(l->d_npartitions); +@@ -189,18 +195,18 @@ static int probe_bsd_pt(blkid_probe pr, + } + + par = blkid_partlist_add_partition(ls, tab, start, size); +- if (!par) +- goto err; ++ if (!par) { ++ rc = -ENOMEM; ++ goto nothing; ++ } + + blkid_partition_set_type(par, p->p_fstype); + } + +- return 0; ++ return BLKID_PROBE_OK; + + nothing: +- return 1; +-err: +- return -1; ++ return rc; + } + + +diff -up util-linux-2.23.2/libblkid/src/partitions/dos.c.kzak util-linux-2.23.2/libblkid/src/partitions/dos.c +--- util-linux-2.23.2/libblkid/src/partitions/dos.c.kzak 2013-07-30 10:39:26.205738229 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/dos.c 2014-03-28 15:13:23.220555797 +0100 +@@ -53,10 +53,13 @@ static int parse_dos_extended(blkid_prob + uint32_t start, size; + + if (++ct_nodata > 100) +- return 0; ++ return BLKID_PROBE_OK; + data = blkid_probe_get_sector(pr, cur_start); +- if (!data) ++ if (!data) { ++ if (errno) ++ return -errno; + goto leave; /* malformed partition? */ ++ } + + if (!is_valid_mbr_signature(data)) + goto leave; +@@ -99,7 +102,7 @@ static int parse_dos_extended(blkid_prob + + par = blkid_partlist_add_partition(ls, tab, abs_start, size); + if (!par) +- goto err; ++ return -ENOMEM; + + blkid_partition_set_type(par, p->sys_type); + blkid_partition_set_flags(par, p->boot_ind); +@@ -123,9 +126,7 @@ static int parse_dos_extended(blkid_prob + cur_size = size; + } + leave: +- return 0; +-err: +- return -1; ++ return BLKID_PROBE_OK; + } + + static int probe_dos_pt(blkid_probe pr, +@@ -140,8 +141,11 @@ static int probe_dos_pt(blkid_probe pr, + uint32_t start, size, id; + + data = blkid_probe_get_sector(pr, 0); +- if (!data) ++ if (!data) { ++ if (errno) ++ return -errno; + goto nothing; ++ } + + /* ignore disks with AIX magic number -- for more details see aix.c */ + if (memcmp(data, BLKID_AIX_MAGIC_STRING, BLKID_AIX_MAGIC_STRLEN) == 0) +@@ -152,7 +156,7 @@ static int probe_dos_pt(blkid_probe pr, + * either the boot sector of a FAT filesystem or a DOS-type + * partition table. + */ +- if (blkid_probe_is_vfat(pr)) { ++ if (blkid_probe_is_vfat(pr) == 1) { + DBG(LOWPROBE, blkid_debug("probably FAT -- ignore")); + goto nothing; + } +@@ -189,6 +193,8 @@ static int probe_dos_pt(blkid_probe pr, + return 0; + + ls = blkid_probe_get_partlist(pr); ++ if (!ls) ++ goto nothing; + + /* sector size factor (the start and size are in the real sectors, but + * we need to convert all sizes to 512 logical sectors +@@ -198,7 +204,7 @@ static int probe_dos_pt(blkid_probe pr, + /* allocate a new partition table */ + tab = blkid_partlist_new_parttable(ls, "dos", BLKID_MSDOS_PT_OFFSET); + if (!tab) +- goto err; ++ return -ENOMEM; + + id = dos_parttable_id(data); + if (id) { +@@ -224,7 +230,7 @@ static int probe_dos_pt(blkid_probe pr, + } + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) +- goto err; ++ return -ENOMEM; + + blkid_partition_set_type(par, p->sys_type); + blkid_partition_set_flags(par, p->boot_ind); +@@ -244,13 +250,14 @@ static int probe_dos_pt(blkid_probe pr, + continue; + if (is_extended(p) && + parse_dos_extended(pr, tab, start, size, ssf) == -1) +- goto err; ++ goto nothing; + } + + /* Parse subtypes (nested partitions) on large disks */ + if (!blkid_probe_is_tiny(pr)) { + for (p = p0, i = 0; i < 4; i++, p++) { + size_t n; ++ int rc; + + if (!dos_partition_size(p) || is_extended(p)) + continue; +@@ -259,20 +266,19 @@ static int probe_dos_pt(blkid_probe pr, + if (dos_nested[n].type != p->sys_type) + continue; + +- if (blkid_partitions_do_subprobe(pr, ++ rc = blkid_partitions_do_subprobe(pr, + blkid_partlist_get_partition(ls, i), +- dos_nested[n].id) == -1) +- goto err; ++ dos_nested[n].id); ++ if (rc < 0) ++ return rc; + break; + } + } + } +- return 0; ++ return BLKID_PROBE_OK; + + nothing: +- return 1; +-err: +- return -1; ++ return BLKID_PROBE_NONE; + } + + +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-03-28 15:11:18.336283724 +0100 ++++ util-linux-2.23.2/libblkid/src/partitions/gpt.c 2014-03-28 15:11:44.677551986 +0100 +@@ -169,8 +169,11 @@ static int is_pmbr_valid(blkid_probe pr, + goto ok; /* skip PMBR check */ + + data = blkid_probe_get_sector(pr, 0); +- if (!data) ++ if (!data) { ++ if (errno) ++ return -errno; + goto failed; ++ } + + if (!is_valid_mbr_signature(data)) + goto failed; +@@ -305,19 +308,27 @@ static int probe_gpt_pt(blkid_probe pr, + uint64_t fu, lu; + uint32_t ssf, i; + efi_guid_t guid; ++ int ret; + + if (last_lba(pr, &lastlba)) + goto nothing; + +- if (!is_pmbr_valid(pr, NULL)) ++ ret = is_pmbr_valid(pr, NULL); ++ if (ret < 0) ++ return ret; ++ else if (ret == 0) + goto nothing; + ++ errno = 0; + h = get_gpt_header(pr, &hdr, &e, (lba = GPT_PRIMARY_LBA), lastlba); +- if (!h) ++ if (!h && !errno) + h = get_gpt_header(pr, &hdr, &e, (lba = lastlba), lastlba); + +- if (!h) ++ if (!h) { ++ if (errno) ++ return -errno; + goto nothing; ++ } + + blkid_probe_use_wiper(pr, lba * blkid_probe_get_size(pr), 8); + +@@ -388,12 +399,13 @@ static int probe_gpt_pt(blkid_probe pr, + blkid_partition_set_flags(par, e->attributes); + } + +- return 0; ++ return BLKID_PROBE_OK; + + nothing: +- return 1; ++ return BLKID_PROBE_NONE; ++ + err: +- return -1; ++ return -ENOMEM; + } + + +diff -up util-linux-2.23.2/libblkid/src/partitions/mac.c.kzak util-linux-2.23.2/libblkid/src/partitions/mac.c +--- util-linux-2.23.2/libblkid/src/partitions/mac.c.kzak 2013-06-13 09:46:10.418650605 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/mac.c 2014-03-28 15:11:44.677551986 +0100 +@@ -87,8 +87,11 @@ static int probe_mac_pt(blkid_probe pr, + * the first block on the disk. + */ + md = (struct mac_driver_desc *) blkid_probe_get_sector(pr, 0); +- if (!md) ++ if (!md) { ++ if (errno) ++ return -errno; + goto nothing; ++ } + + block_size = be16_to_cpu(md->block_size); + +@@ -96,8 +99,11 @@ static int probe_mac_pt(blkid_probe pr, + * the second block on the disk. + */ + p = (struct mac_partition *) get_mac_block(pr, block_size, 1); +- if (!p) ++ if (!p) { ++ if (errno) ++ return -errno; + goto nothing; ++ } + + /* check the first partition signature */ + if (!has_part_signature(p)) +@@ -109,7 +115,7 @@ static int probe_mac_pt(blkid_probe pr, + + ls = blkid_probe_get_partlist(pr); + if (!ls) +- goto err; ++ goto nothing; + + tab = blkid_partlist_new_parttable(ls, "mac", 0); + if (!tab) +@@ -124,15 +130,18 @@ static int probe_mac_pt(blkid_probe pr, + uint32_t size; + + p = (struct mac_partition *) get_mac_block(pr, block_size, i); +- if (!p) ++ if (!p) { ++ if (errno) ++ return -errno; + goto nothing; ++ } + if (!has_part_signature(p)) + goto nothing; + + if (be32_to_cpu(p->map_count) != nblks) { + DBG(LOWPROBE, blkid_debug( + "mac: inconsisten map_count in partition map, " +- "entry[0]: %d, entry[%d]: %d", ++ "entry[0]: %d, entry[%d]: %d", + nblks, i - 1, + be32_to_cpu(p->map_count))); + } +@@ -157,12 +166,12 @@ static int probe_mac_pt(blkid_probe pr, + sizeof(p->type)); + } + +- return 0; ++ return BLKID_PROBE_OK; + + nothing: +- return 1; ++ return BLKID_PROBE_NONE; + err: +- return -1; ++ return -ENOMEM; + } + + /* +diff -up util-linux-2.23.2/libblkid/src/partitions/minix.c.kzak util-linux-2.23.2/libblkid/src/partitions/minix.c +--- util-linux-2.23.2/libblkid/src/partitions/minix.c.kzak 2013-07-15 10:25:46.284049064 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/minix.c 2014-03-28 15:36:23.866715100 +0100 +@@ -26,12 +26,15 @@ static int probe_minix_pt(blkid_probe pr + int i; + + data = blkid_probe_get_sector(pr, 0); +- if (!data) ++ if (!data) { ++ if (errno) ++ return -errno; + goto nothing; ++ } + + ls = blkid_probe_get_partlist(pr); + if (!ls) +- goto err; ++ goto nothing; + + /* Parent is required, because Minix uses the same PT as DOS and + * difference is only in primary partition (parent) type. +@@ -78,12 +81,12 @@ static int probe_minix_pt(blkid_probe pr + blkid_partition_set_flags(par, p->boot_ind); + } + +- return 0; ++ return BLKID_PROBE_OK; + + nothing: +- return 1; ++ return BLKID_PROBE_NONE; + err: +- return -1; ++ return -ENOMEM; + } + + /* same as DOS */ +diff -up util-linux-2.23.2/libblkid/src/partitions/partitions.c.kzak util-linux-2.23.2/libblkid/src/partitions/partitions.c +--- util-linux-2.23.2/libblkid/src/partitions/partitions.c.kzak 2014-03-28 15:11:18.336283724 +0100 ++++ util-linux-2.23.2/libblkid/src/partitions/partitions.c 2014-03-28 15:12:28.036993622 +0100 +@@ -533,12 +533,13 @@ static int idinfo_probe(blkid_probe pr, + { + const struct blkid_idmag *mag = NULL; + blkid_loff_t off; +- int rc = 1; /* = nothing detected */ ++ int rc = BLKID_PROBE_NONE; /* = nothing detected */ + + if (pr->size <= 0 || (id->minsz && id->minsz > pr->size)) + goto nothing; /* the device is too small */ + +- if (blkid_probe_get_idmag(pr, id, &off, &mag)) ++ rc = blkid_probe_get_idmag(pr, id, &off, &mag); ++ if (rc != BLKID_PROBE_OK) + goto nothing; + + /* final check by probing function */ +@@ -546,14 +547,15 @@ static int idinfo_probe(blkid_probe pr, + DBG(LOWPROBE, blkid_debug( + "%s: ---> call probefunc()", id->name)); + rc = id->probefunc(pr, mag); +- if (rc == -1) { ++ if (rc < 0) { + /* reset after error */ + reset_partlist(blkid_probe_get_partlist(pr)); + if (chn && !chn->binary) + blkid_probe_chain_reset_vals(pr, chn); +- DBG(LOWPROBE, blkid_debug("%s probefunc failed", id->name)); ++ DBG(LOWPROBE, blkid_debug("%s probefunc failed, rc %d", ++ id->name, rc)); + } +- if (rc == 0 && mag && chn && !chn->binary) ++ if (rc == BLKID_PROBE_OK && mag && chn && !chn->binary) + rc = blkid_probe_set_magic(pr, off, mag->len, + (unsigned char *) mag->magic); + +@@ -569,11 +571,11 @@ nothing: + */ + static int partitions_probe(blkid_probe pr, struct blkid_chain *chn) + { +- int rc = 1; ++ int rc = BLKID_PROBE_NONE; + size_t i; + + if (!pr || chn->idx < -1) +- return -1; ++ return -EINVAL; + blkid_probe_chain_reset_vals(pr, chn); + + if (chn->binary) +@@ -597,7 +599,10 @@ static int partitions_probe(blkid_probe + continue; + + /* apply checks from idinfo */ +- if (idinfo_probe(pr, idinfos[i], chn) != 0) ++ rc = idinfo_probe(pr, idinfos[i], chn); ++ if (rc < 0) ++ break; ++ if (rc != BLKID_PROBE_OK) + continue; + + name = idinfos[i]->name; +@@ -613,20 +618,21 @@ static int partitions_probe(blkid_probe + break; + } + +- if (rc == 1) { +- DBG(LOWPROBE, blkid_debug("<-- leaving probing loop (failed) [PARTS idx=%d]", +- chn->idx)); ++ if (rc != BLKID_PROBE_OK) { ++ DBG(LOWPROBE, blkid_debug("<-- leaving probing loop (failed=%d) [PARTS idx=%d]", ++ rc, chn->idx)); + } + + details_only: + /* + * Gather PART_ENTRY_* values if the current device is a partition. + */ +- if (!chn->binary && ++ if ((rc == BLKID_PROBE_OK || rc == BLKID_PROBE_NONE) && !chn->binary && + (blkid_partitions_get_flags(pr) & BLKID_PARTS_ENTRY_DETAILS)) { + +- if (!blkid_partitions_probe_partition(pr)) +- rc = 0; ++ int xrc = blkid_partitions_probe_partition(pr); ++ if (xrc < 0) ++ rc = xrc; /* optional, care about errors only */ + } + + return rc; +@@ -637,7 +643,7 @@ int blkid_partitions_do_subprobe(blkid_p + const struct blkid_idinfo *id) + { + blkid_probe prc; +- int rc = 1; ++ int rc; + blkid_partlist ls; + blkid_loff_t sz, off; + +@@ -646,7 +652,7 @@ int blkid_partitions_do_subprobe(blkid_p + id->name, parent)); + + if (!pr || !parent || !parent->size) +- return -1; ++ return -EINVAL; + + /* range defined by parent */ + sz = ((blkid_loff_t) parent->size) << 9; +@@ -656,13 +662,13 @@ int blkid_partitions_do_subprobe(blkid_p + DBG(LOWPROBE, blkid_debug( + "ERROR: parts: <---- '%s' subprobe: overflow detected.", + id->name)); +- return -1; ++ return -ENOSPC; + } + + /* create private prober */ + prc = blkid_clone_probe(pr); + if (!prc) +- return -1; ++ return -ENOMEM; + + blkid_probe_set_dimension(prc, off, sz); + +@@ -695,7 +701,7 @@ int blkid_partitions_do_subprobe(blkid_p + + static int blkid_partitions_probe_partition(blkid_probe pr) + { +- int rc = 1; ++ int rc = BLKID_PROBE_NONE; + blkid_probe disk_pr = NULL; + blkid_partlist ls; + blkid_partition par; +@@ -761,7 +767,7 @@ static int blkid_partitions_probe_partit + blkid_probe_sprintf_value(pr, "PART_ENTRY_DISK", "%u:%u", + major(disk), minor(disk)); + } +- rc = 0; ++ rc = BLKID_PROBE_OK; + nothing: + return rc; + } +diff -up util-linux-2.23.2/libblkid/src/partitions/sgi.c.kzak util-linux-2.23.2/libblkid/src/partitions/sgi.c +--- util-linux-2.23.2/libblkid/src/partitions/sgi.c.kzak 2013-07-15 10:25:46.285049072 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/sgi.c 2014-03-28 15:11:44.677551986 +0100 +@@ -99,8 +99,11 @@ static int probe_sgi_pt(blkid_probe pr, + int i; + + l = (struct sgi_disklabel *) blkid_probe_get_sector(pr, 0); +- if (!l) ++ if (!l) { ++ if (errno) ++ return -errno; + goto nothing; ++ } + + if (count_checksum(l)) { + DBG(LOWPROBE, blkid_debug( +@@ -110,11 +113,11 @@ static int probe_sgi_pt(blkid_probe pr, + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ +- return 0; ++ return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) +- goto err; ++ goto nothing; + + tab = blkid_partlist_new_parttable(ls, "sgi", 0); + if (!tab) +@@ -138,12 +141,12 @@ static int probe_sgi_pt(blkid_probe pr, + blkid_partition_set_type(par, type); + } + +- return 0; ++ return BLKID_PROBE_OK; + + nothing: +- return 1; ++ return BLKID_PROBE_NONE; + err: +- return -1; ++ return -ENOMEM; + } + + const struct blkid_idinfo sgi_pt_idinfo = +diff -up util-linux-2.23.2/libblkid/src/partitions/solaris_x86.c.kzak util-linux-2.23.2/libblkid/src/partitions/solaris_x86.c +--- util-linux-2.23.2/libblkid/src/partitions/solaris_x86.c.kzak 2013-06-13 09:46:10.418650605 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/solaris_x86.c 2014-03-28 15:11:44.677551986 +0100 +@@ -69,8 +69,11 @@ static int probe_solaris_pt(blkid_probe + uint16_t nparts; + + l = (struct solaris_vtoc *) blkid_probe_get_sector(pr, SOLARIS_SECTOR); +- if (!l) ++ if (!l) { ++ if (errno) ++ return -errno; + goto nothing; ++ } + + if (le32_to_cpu(l->v_version) != 1) { + DBG(LOWPROBE, blkid_debug( +@@ -81,11 +84,11 @@ static int probe_solaris_pt(blkid_probe + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ +- return 0; ++ return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) +- goto err; ++ goto nothing; + + parent = blkid_partlist_get_parent(ls); + +@@ -126,12 +129,12 @@ static int probe_solaris_pt(blkid_probe + blkid_partition_set_flags(par, le16_to_cpu(p->s_flag)); + } + +- return 0; ++ return BLKID_PROBE_OK; + + nothing: +- return 1; ++ return BLKID_PROBE_NONE; + err: +- return -1; ++ return -ENOMEM; + } + + const struct blkid_idinfo solaris_x86_pt_idinfo = +diff -up util-linux-2.23.2/libblkid/src/partitions/sun.c.kzak util-linux-2.23.2/libblkid/src/partitions/sun.c +--- util-linux-2.23.2/libblkid/src/partitions/sun.c.kzak 2013-06-13 09:46:10.419650613 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/sun.c 2014-03-28 15:11:44.678551996 +0100 +@@ -27,8 +27,11 @@ static int probe_sun_pt(blkid_probe pr, + int i, use_vtoc; + + l = (struct sun_disklabel *) blkid_probe_get_sector(pr, 0); +- if (!l) ++ if (!l) { ++ if (errno) ++ return -errno; + goto nothing; ++ } + + if (sun_pt_checksum(l)) { + DBG(LOWPROBE, blkid_debug( +@@ -38,11 +41,11 @@ static int probe_sun_pt(blkid_probe pr, + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ +- return 0; ++ return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) +- goto err; ++ goto nothing; + + tab = blkid_partlist_new_parttable(ls, "sun", 0); + if (!tab) +@@ -76,7 +79,7 @@ static int probe_sun_pt(blkid_probe pr, + uint16_t type = 0, flags = 0; + blkid_partition par; + +- start = be32_to_cpu(p->start_cylinder) * spc; ++ start = be32_to_cpu(p->start_cylinder) * spc; + size = be32_to_cpu(p->num_sectors); + if (use_vtoc) { + type = be16_to_cpu(l->vtoc.infos[i].id); +@@ -96,12 +99,12 @@ static int probe_sun_pt(blkid_probe pr, + if (flags) + blkid_partition_set_flags(par, flags); + } +- return 0; ++ return BLKID_PROBE_OK; + + nothing: +- return 1; ++ return BLKID_PROBE_NONE; + err: +- return -1; ++ return -ENOMEM; + } + + +diff -up util-linux-2.23.2/libblkid/src/partitions/ultrix.c.kzak util-linux-2.23.2/libblkid/src/partitions/ultrix.c +--- util-linux-2.23.2/libblkid/src/partitions/ultrix.c.kzak 2013-05-30 15:21:43.127591380 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/ultrix.c 2014-03-28 15:11:44.678551996 +0100 +@@ -44,8 +44,11 @@ static int probe_ultrix_pt(blkid_probe p + int i; + + data = blkid_probe_get_sector(pr, ULTRIX_SECTOR); +- if (!data) ++ if (!data) { ++ if (errno) ++ return -errno; + goto nothing; ++ } + + l = (struct ultrix_disklabel *) (data + ULTRIX_OFFSET); + +@@ -59,11 +62,11 @@ static int probe_ultrix_pt(blkid_probe p + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ +- return 0; ++ return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) +- goto err; ++ goto nothing; + + tab = blkid_partlist_new_parttable(ls, "ultrix", 0); + if (!tab) +@@ -80,11 +83,11 @@ static int probe_ultrix_pt(blkid_probe p + } + } + +- return 0; ++ return BLKID_PROBE_OK; + nothing: +- return 1; ++ return BLKID_PROBE_NONE; + err: +- return -1; ++ return -ENOMEM; + } + + const struct blkid_idinfo ultrix_pt_idinfo = +diff -up util-linux-2.23.2/libblkid/src/partitions/unixware.c.kzak util-linux-2.23.2/libblkid/src/partitions/unixware.c +--- util-linux-2.23.2/libblkid/src/partitions/unixware.c.kzak 2013-06-13 09:46:10.419650613 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/unixware.c 2014-03-28 15:11:44.678551996 +0100 +@@ -106,19 +106,22 @@ static int probe_unixware_pt(blkid_probe + + l = (struct unixware_disklabel *) + blkid_probe_get_sector(pr, UNIXWARE_SECTOR); +- if (!l) ++ if (!l) { ++ if (errno) ++ return -errno; + goto nothing; ++ } + + if (le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_VTOCMAGIC) + goto nothing; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ +- return 0; ++ return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) +- goto err; ++ goto nothing; + + parent = blkid_partlist_get_parent(ls); + +@@ -161,12 +164,12 @@ static int probe_unixware_pt(blkid_probe + blkid_partition_set_flags(par, flg); + } + +- return 0; ++ return BLKID_PROBE_OK; + + nothing: +- return 1; ++ return BLKID_PROBE_NONE; + err: +- return -1; ++ return -ENOMEM; + } + + +diff -up util-linux-2.23.2/libblkid/src/probe.c.kzak util-linux-2.23.2/libblkid/src/probe.c +--- util-linux-2.23.2/libblkid/src/probe.c.kzak 2014-03-28 15:11:18.334283704 +0100 ++++ util-linux-2.23.2/libblkid/src/probe.c 2014-03-28 15:11:44.678551996 +0100 +@@ -559,13 +559,17 @@ unsigned char *blkid_probe_get_buffer(bl + if (!bf) { + ssize_t ret; + +- if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0) ++ if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0) { ++ errno = 0; + return NULL; ++ } + + /* allocate info and space for data by why call */ + bf = calloc(1, sizeof(struct blkid_bufinfo) + len); +- if (!bf) ++ if (!bf) { ++ errno = ENOMEM; + return NULL; ++ } + + bf->data = ((unsigned char *) bf) + sizeof(struct blkid_bufinfo); + bf->len = len; +@@ -577,7 +581,10 @@ unsigned char *blkid_probe_get_buffer(bl + + ret = read(pr->fd, bf->data, len); + if (ret != (ssize_t) len) { ++ DBG(LOWPROBE, blkid_debug("\tbuffer read: return %zd error %m", ret)); + free(bf); ++ if (ret >= 0) ++ errno = 0; + return NULL; + } + list_add_tail(&bf->bufs, &pr->buffers); +@@ -766,6 +773,17 @@ int blkid_probe_set_dimension(blkid_prob + return 0; + } + ++/** ++ * blkid_probe_get_idmag: ++ * @pr: probe ++ * @id: id information ++ * @offset: begin of probing area ++ * @res: found id information ++ * ++ * Check for matching magic value. ++ * Returns BLKID_PROBE_OK if found, BLKID_PROBE_NONE if not found ++ * or no magic present, or negative value on error. ++ */ + int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id, + blkid_loff_t *offset, const struct blkid_idmag **res) + { +@@ -784,6 +802,8 @@ int blkid_probe_get_idmag(blkid_probe pr + off = (mag->kboff + (mag->sboff >> 10)) << 10; + buf = blkid_probe_get_buffer(pr, off, 1024); + ++ if (!buf && errno) ++ return -errno; + if (buf && !memcmp(mag->magic, + buf + (mag->sboff & 0x3ff), mag->len)) { + DBG(LOWPROBE, blkid_debug("\tmagic sboff=%u, kboff=%ld", +@@ -792,16 +812,16 @@ int blkid_probe_get_idmag(blkid_probe pr + *offset = off + (mag->sboff & 0x3ff); + if (res) + *res = mag; +- return 0; ++ return BLKID_PROBE_OK; + } + mag++; + } + + if (id && id->magics[0].magic) + /* magic string(s) defined, but not found */ +- return 1; ++ return BLKID_PROBE_NONE; + +- return 0; ++ return BLKID_PROBE_OK; + } + + static inline void blkid_probe_start(blkid_probe pr) +diff -up util-linux-2.23.2/libblkid/src/superblocks/adaptec_raid.c.kzak util-linux-2.23.2/libblkid/src/superblocks/adaptec_raid.c +--- util-linux-2.23.2/libblkid/src/superblocks/adaptec_raid.c.kzak 2013-04-08 17:55:40.232855970 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/adaptec_raid.c 2014-03-28 15:11:44.678551996 +0100 +@@ -80,10 +80,10 @@ static int probe_adraid(blkid_probe pr, + struct adaptec_metadata *ad; + + if (pr->size < 0x10000) +- return -1; ++ return BLKID_PROBE_NONE; + + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) +- return -1; ++ return BLKID_PROBE_NONE; + + off = ((pr->size / 0x200)-1) * 0x200; + ad = (struct adaptec_metadata *) +@@ -91,17 +91,19 @@ static int probe_adraid(blkid_probe pr, + off, + sizeof(struct adaptec_metadata)); + if (!ad) +- return -1; ++ return errno ? -errno : BLKID_PROBE_NONE;; ++ + if (ad->smagic != be32_to_cpu(AD_SIGNATURE)) +- return -1; ++ return BLKID_PROBE_NONE; + if (ad->b0idcode != be32_to_cpu(AD_MAGIC)) +- return -1; ++ return BLKID_PROBE_NONE; + if (blkid_probe_sprintf_version(pr, "%u", ad->resver) != 0) +- return -1; ++ return BLKID_PROBE_NONE; + if (blkid_probe_set_magic(pr, off, sizeof(ad->b0idcode), + (unsigned char *) &ad->b0idcode)) +- return -1; +- return 0; ++ return BLKID_PROBE_NONE; ++ ++ return BLKID_PROBE_OK; + } + + const struct blkid_idinfo adraid_idinfo = { +diff -up util-linux-2.23.2/libblkid/src/superblocks/befs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/befs.c +--- util-linux-2.23.2/libblkid/src/superblocks/befs.c.kzak 2013-04-08 17:55:40.237856017 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/befs.c 2014-03-28 15:11:44.679552006 +0100 +@@ -261,21 +261,23 @@ static int64_t get_key_value(blkid_probe + int64_t node_pointer; + int32_t first, last, mid, cmp; + ++ errno = 0; + bh = (struct bplustree_header *) get_tree_node(pr, bs, &bi->data, 0, + sizeof(struct bplustree_header), fs_le); + if (!bh) +- return -1; ++ return errno ? -errno : -ENOENT; + + if ((int32_t) FS32_TO_CPU(bh->magic, fs_le) != BPLUSTREE_MAGIC) +- return -1; ++ return -ENOENT; + + node_pointer = FS64_TO_CPU(bh->root_node_pointer, fs_le); + + do { ++ errno = 0; + bn = (struct bplustree_node *) get_tree_node(pr, bs, &bi->data, + node_pointer, FS32_TO_CPU(bh->node_size, fs_le), fs_le); + if (!bn) +- return -1; ++ return errno ? -errno : -ENOENT; + + keylengths = (uint16_t *) ((uint8_t *) bn + + ((sizeof(struct bplustree_node) +@@ -336,10 +338,10 @@ static int get_uuid(blkid_probe pr, cons + + bi = (struct befs_inode *) get_block_run(pr, bs, &bs->root_dir, fs_le); + if (!bi) +- return -1; ++ return errno ? -errno : BLKID_PROBE_NONE; + + if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1) +- return -1; ++ return BLKID_PROBE_NONE; + + sd = (struct small_data *) bi->small_data; + +@@ -376,24 +378,24 @@ static int get_uuid(blkid_probe pr, cons + bi = (struct befs_inode *) get_block_run(pr, bs, + &bi->attributes, fs_le); + if (!bi) +- return -1; ++ return errno ? -errno : BLKID_PROBE_NONE; + + if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1) +- return -1; ++ return BLKID_PROBE_NONE; + + value = get_key_value(pr, bs, bi, KEY_NAME, fs_le); +- + if (value < 0) +- return value; ++ return value == -ENOENT ? BLKID_PROBE_NONE : value; ++ + else if (value > 0) { + bi = (struct befs_inode *) blkid_probe_get_buffer(pr, + value << FS32_TO_CPU(bs->block_shift, fs_le), + FS32_TO_CPU(bs->block_size, fs_le)); + if (!bi) +- return -1; ++ return errno ? -errno : BLKID_PROBE_NONE; + + if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1) +- return -1; ++ return 1; + + if (FS32_TO_CPU(bi->type, fs_le) == B_UINT64_TYPE + && FS64_TO_CPU(bi->data.size, fs_le) == KEY_SIZE +@@ -404,7 +406,7 @@ static int get_uuid(blkid_probe pr, cons + attr_data = (uint64_t *) get_block_run(pr, bs, + &bi->data.direct[0], fs_le); + if (!attr_data) +- return -1; ++ return errno ? -errno : BLKID_PROBE_NONE; + + *uuid = *attr_data; + } +@@ -424,7 +426,7 @@ static int probe_befs(blkid_probe pr, co + mag->sboff - B_OS_NAME_LENGTH, + sizeof(struct befs_super_block)); + if (!bs) +- return -1; ++ return errno ? -errno : BLKID_PROBE_NONE; + + if (le32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1 + && le32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2 +@@ -439,11 +441,11 @@ static int probe_befs(blkid_probe pr, co + fs_le = 0; + version = "big-endian"; + } else +- return -1; ++ return BLKID_PROBE_NONE; + + ret = get_uuid(pr, bs, &volume_id, fs_le); + +- if (ret < 0) ++ if (ret != 0) + return ret; + + /* +@@ -459,7 +461,7 @@ static int probe_befs(blkid_probe pr, co + blkid_probe_sprintf_uuid(pr, (unsigned char *) &volume_id, + sizeof(volume_id), "%016" PRIx64, + FS64_TO_CPU(volume_id, fs_le)); +- return 0; ++ return BLKID_PROBE_OK; + } + + const struct blkid_idinfo befs_idinfo = +diff -up util-linux-2.23.2/libblkid/src/superblocks/btrfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/btrfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/btrfs.c.kzak 2014-03-28 15:11:18.335283714 +0100 ++++ util-linux-2.23.2/libblkid/src/superblocks/btrfs.c 2014-03-28 15:11:44.679552006 +0100 +@@ -65,7 +65,7 @@ static int probe_btrfs(blkid_probe pr, c + + bfs = blkid_probe_get_sb(pr, mag, struct btrfs_super_block); + if (!bfs) +- return -1; ++ return errno ? -errno : 1; + + if (*bfs->label) + blkid_probe_set_label(pr, +diff -up util-linux-2.23.2/libblkid/src/superblocks/cramfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/cramfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/cramfs.c.kzak 2013-04-08 17:55:40.240856046 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/cramfs.c 2014-03-28 15:11:44.679552006 +0100 +@@ -40,7 +40,7 @@ static int probe_cramfs(blkid_probe pr, + + cs = blkid_probe_get_sb(pr, mag, struct cramfs_super); + if (!cs) +- return -1; ++ return errno ? -errno : 1; + + blkid_probe_set_label(pr, cs->name, sizeof(cs->name)); + return 0; +diff -up util-linux-2.23.2/libblkid/src/superblocks/ddf_raid.c.kzak util-linux-2.23.2/libblkid/src/superblocks/ddf_raid.c +--- util-linux-2.23.2/libblkid/src/superblocks/ddf_raid.c.kzak 2013-04-08 17:55:40.241856056 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/ddf_raid.c 2014-03-28 15:11:44.679552006 +0100 +@@ -81,7 +81,7 @@ static int probe_ddf(blkid_probe pr, + uint64_t off, lba; + + if (pr->size < 0x30000) +- return -1; ++ return 1; + + for (i = 0; i < ARRAY_SIZE(hdrs); i++) { + off = ((pr->size / 0x200) - hdrs[i]) * 0x200; +@@ -90,8 +90,7 @@ static int probe_ddf(blkid_probe pr, + off, + sizeof(struct ddf_header)); + if (!ddf) +- return -1; +- ++ return errno ? -errno : 1; + if (ddf->signature == cpu_to_be32(DDF_MAGIC) || + ddf->signature == cpu_to_le32(DDF_MAGIC)) + break; +@@ -99,7 +98,7 @@ static int probe_ddf(blkid_probe pr, + } + + if (!ddf) +- return -1; ++ return 1; + + lba = ddf->signature == cpu_to_be32(DDF_MAGIC) ? + be64_to_cpu(ddf->primary_lba) : +@@ -111,8 +110,12 @@ static int probe_ddf(blkid_probe pr, + + buf = blkid_probe_get_buffer(pr, + lba << 9, sizeof(ddf->signature)); +- if (!buf || memcmp(buf, &ddf->signature, 4)) +- return -1; ++ if (!buf) { ++ if (errno) ++ return -errno; ++ if (memcmp(buf, &ddf->signature, 4)) ++ return 1; ++ } + } + + blkid_probe_strncpy_uuid(pr, ddf->guid, sizeof(ddf->guid)); +@@ -121,11 +124,11 @@ static int probe_ddf(blkid_probe pr, + *(version + sizeof(ddf->ddf_rev)) = '\0'; + + if (blkid_probe_set_version(pr, version) != 0) +- return -1; ++ return 1; + if (blkid_probe_set_magic(pr, off, + sizeof(ddf->signature), + (unsigned char *) &ddf->signature)) +- return -1; ++ return 1; + return 0; + } + +diff -up util-linux-2.23.2/libblkid/src/superblocks/drbd.c.kzak util-linux-2.23.2/libblkid/src/superblocks/drbd.c +--- util-linux-2.23.2/libblkid/src/superblocks/drbd.c.kzak 2013-04-08 17:55:40.243856074 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/drbd.c 2014-03-28 15:11:44.679552006 +0100 +@@ -75,18 +75,18 @@ static int probe_drbd(blkid_probe pr, + + /* Small devices cannot be drbd (?) */ + if (pr->size < 0x10000) +- return -1; ++ return 1; + + md = (struct md_on_disk_08 *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct md_on_disk_08)); + if (!md) +- return -1; ++ return errno ? -errno : 1; + + if (be32_to_cpu(md->magic) != DRBD_MD_MAGIC_08 && + be32_to_cpu(md->magic) != DRBD_MD_MAGIC_84_UNCLEAN) +- return -1; ++ return 1; + + /* + * DRBD does not have "real" uuids; the following resembles DRBD's +@@ -102,7 +102,7 @@ static int probe_drbd(blkid_probe pr, + off + offsetof(struct md_on_disk_08, magic), + sizeof(md->magic), + (unsigned char *) &md->magic)) +- return -1; ++ return 1; + + return 0; + } +diff -up util-linux-2.23.2/libblkid/src/superblocks/drbdproxy_datalog.c.kzak util-linux-2.23.2/libblkid/src/superblocks/drbdproxy_datalog.c +--- util-linux-2.23.2/libblkid/src/superblocks/drbdproxy_datalog.c.kzak 2013-04-08 17:55:40.244856084 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/drbdproxy_datalog.c 2014-03-28 15:11:44.679552006 +0100 +@@ -33,7 +33,7 @@ static int probe_drbdproxy_datalog(blkid + + lh = (struct log_header_t *) blkid_probe_get_buffer(pr, 0, sizeof(*lh)); + if (!lh) +- return -1; ++ return errno ? -errno : 1; + + blkid_probe_set_uuid(pr, lh->uuid); + blkid_probe_sprintf_version(pr, "v%jd", le64_to_cpu(lh->version)); +diff -up util-linux-2.23.2/libblkid/src/superblocks/exfat.c.kzak util-linux-2.23.2/libblkid/src/superblocks/exfat.c +--- util-linux-2.23.2/libblkid/src/superblocks/exfat.c.kzak 2013-04-08 17:55:40.245856093 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/exfat.c 2014-03-28 15:11:44.680552016 +0100 +@@ -115,12 +115,14 @@ static int probe_exfat(blkid_probe pr, c + + sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block); + if (!sb) +- return -1; ++ return errno ? -errno : BLKID_PROBE_NONE; + + label = find_label(pr, sb); + if (label) + blkid_probe_set_utf8label(pr, label->name, + min(label->length * 2, 30), BLKID_ENC_UTF16LE); ++ else if (errno) ++ return -errno; + + blkid_probe_sprintf_uuid(pr, sb->volume_serial, 4, + "%02hhX%02hhX-%02hhX%02hhX", +@@ -130,7 +132,7 @@ static int probe_exfat(blkid_probe pr, c + blkid_probe_sprintf_version(pr, "%u.%u", + sb->version.major, sb->version.minor); + +- return 0; ++ return BLKID_PROBE_OK; + } + + const struct blkid_idinfo exfat_idinfo = +diff -up util-linux-2.23.2/libblkid/src/superblocks/ext.c.kzak util-linux-2.23.2/libblkid/src/superblocks/ext.c +--- util-linux-2.23.2/libblkid/src/superblocks/ext.c.kzak 2014-03-28 15:11:18.333283694 +0100 ++++ util-linux-2.23.2/libblkid/src/superblocks/ext.c 2014-03-28 15:11:44.680552016 +0100 +@@ -198,9 +198,9 @@ static int probe_jbd(blkid_probe pr, + + es = ext_get_super(pr, NULL, &fi, NULL); + if (!es) +- return -BLKID_ERR_PARAM; ++ return errno ? -errno : 1; + if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) +- return -BLKID_ERR_PARAM; ++ return 1; + + ext_get_info(pr, 2, es); + return 0; +@@ -214,16 +214,16 @@ static int probe_ext2(blkid_probe pr, + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) +- return -BLKID_ERR_PARAM; ++ return errno ? -errno : 1; + + /* Distinguish between ext3 and ext2 */ + if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) +- return -BLKID_ERR_PARAM; ++ return 1; + + /* Any features which ext2 doesn't understand */ + if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) || + (fi & EXT2_FEATURE_INCOMPAT_UNSUPPORTED)) +- return -BLKID_ERR_PARAM; ++ return 1; + + ext_get_info(pr, 2, es); + return 0; +@@ -237,16 +237,16 @@ static int probe_ext3(blkid_probe pr, + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) +- return -BLKID_ERR_PARAM; ++ return errno ? -errno : 1; + + /* ext3 requires journal */ + if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) +- return -BLKID_ERR_PARAM; ++ return 1; + + /* Any features which ext3 doesn't understand */ + if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) || + (fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) +- return -BLKID_ERR_PARAM; ++ return 1; + + ext_get_info(pr, 3, es); + return 0; +@@ -261,14 +261,14 @@ static int probe_ext4dev(blkid_probe pr, + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) +- return -BLKID_ERR_PARAM; ++ return errno ? -errno : 1; + + /* Distinguish from jbd */ + if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) +- return -BLKID_ERR_PARAM; ++ return 1; + + if (!(le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS)) +- return -BLKID_ERR_PARAM; ++ return 1; + + ext_get_info(pr, 4, es); + return 0; +@@ -282,16 +282,16 @@ static int probe_ext4(blkid_probe pr, + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) +- return -1; ++ return errno ? -errno : 1; + + /* Distinguish from jbd */ + if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) +- return -BLKID_ERR_PARAM; ++ return 1; + + /* Ext4 has at least one feature which ext3 doesn't understand */ + if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) && + !(fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) +- return -BLKID_ERR_PARAM; ++ return 1; + + /* + * If the filesystem is a OK for use by in-development +@@ -304,7 +304,7 @@ static int probe_ext4(blkid_probe pr, + * ext4dev. + */ + if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) +- return -BLKID_ERR_PARAM; ++ return 1; + + ext_get_info(pr, 4, es); + return 0; +diff -up util-linux-2.23.2/libblkid/src/superblocks/f2fs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/f2fs.c +--- util-linux-2.23.2/libblkid/src/superblocks/f2fs.c.kzak 2013-06-13 09:46:10.422650639 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/f2fs.c 2014-03-28 15:11:44.680552016 +0100 +@@ -62,7 +62,7 @@ static int probe_f2fs(blkid_probe pr, co + + sb = blkid_probe_get_sb(pr, mag, struct f2fs_super_block); + if (!sb) +- return -1; ++ return errno ? -errno : 1; + + major = le16_to_cpu(sb->major_ver); + minor = le16_to_cpu(sb->minor_ver); +diff -up util-linux-2.23.2/libblkid/src/superblocks/gfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/gfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/gfs.c.kzak 2013-04-08 17:55:40.250856141 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/gfs.c 2014-03-28 15:11:44.680552016 +0100 +@@ -64,7 +64,7 @@ static int probe_gfs(blkid_probe pr, con + + sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb); + if (!sbd) +- return -1; ++ return errno ? -errno : 1; + + if (be32_to_cpu(sbd->sb_fs_format) == GFS_FORMAT_FS && + be32_to_cpu(sbd->sb_multihost_format) == GFS_FORMAT_MULTI) +@@ -78,7 +78,7 @@ static int probe_gfs(blkid_probe pr, con + return 0; + } + +- return -1; ++ return 1; + } + + static int probe_gfs2(blkid_probe pr, const struct blkid_idmag *mag) +@@ -87,7 +87,7 @@ static int probe_gfs2(blkid_probe pr, co + + sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb); + if (!sbd) +- return -1; ++ return errno ? -errno : 1; + + if (be32_to_cpu(sbd->sb_fs_format) == GFS2_FORMAT_FS && + be32_to_cpu(sbd->sb_multihost_format) == GFS2_FORMAT_MULTI) +@@ -100,7 +100,7 @@ static int probe_gfs2(blkid_probe pr, co + blkid_probe_set_version(pr, "1"); + return 0; + } +- return -1; ++ return 1; + } + + const struct blkid_idinfo gfs_idinfo = +diff -up util-linux-2.23.2/libblkid/src/superblocks/hfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/hfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/hfs.c.kzak 2013-04-08 17:55:40.251856150 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/hfs.c 2014-03-28 15:11:44.680552016 +0100 +@@ -154,7 +154,7 @@ static int probe_hfs(blkid_probe pr, con + + hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb); + if (!hfs) +- return -1; ++ return errno ? -errno : 1; + + if ((memcmp(hfs->embed_sig, "H+", 2) == 0) || + (memcmp(hfs->embed_sig, "HX", 2) == 0)) +@@ -193,7 +193,7 @@ static int probe_hfsplus(blkid_probe pr, + + sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb); + if (!sbd) +- return -1; ++ return errno ? -errno : 1; + + /* Check for a HFS+ volume embedded in a HFS volume */ + if (memcmp(sbd->signature, "BD", 2) == 0) { +@@ -218,7 +218,7 @@ static int probe_hfsplus(blkid_probe pr, + struct hfsplus_vol_header); + + if (!hfsplus) +- return -1; ++ return errno ? -errno : 1; + + if ((memcmp(hfsplus->signature, "H+", 2) != 0) && + (memcmp(hfsplus->signature, "HX", 2) != 0)) +@@ -228,7 +228,7 @@ static int probe_hfsplus(blkid_probe pr, + + blocksize = be32_to_cpu(hfsplus->blocksize); + if (blocksize < HFSPLUS_SECTOR_SIZE) +- return -1; ++ return 1; + + memcpy(extents, hfsplus->cat_file.extents, sizeof(extents)); + cat_block = be32_to_cpu(extents[0].start_block); +@@ -236,7 +236,7 @@ static int probe_hfsplus(blkid_probe pr, + buf = blkid_probe_get_buffer(pr, + off + ((blkid_loff_t) cat_block * blocksize), 0x2000); + if (!buf) +- return 0; ++ return errno ? -errno : 0; + + bnode = (struct hfsplus_bheader_record *) + &buf[sizeof(struct hfsplus_bnode_descriptor)]; +@@ -271,7 +271,7 @@ static int probe_hfsplus(blkid_probe pr, + (blkid_loff_t) off + leaf_off, + leaf_node_size); + if (!buf) +- return 0; ++ return errno ? -errno : 0; + + descr = (struct hfsplus_bnode_descriptor *) buf; + record_count = be16_to_cpu(descr->num_recs); +diff -up util-linux-2.23.2/libblkid/src/superblocks/highpoint_raid.c.kzak util-linux-2.23.2/libblkid/src/superblocks/highpoint_raid.c +--- util-linux-2.23.2/libblkid/src/superblocks/highpoint_raid.c.kzak 2013-04-08 17:55:40.251856150 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/highpoint_raid.c 2014-03-28 15:11:44.680552016 +0100 +@@ -30,9 +30,9 @@ static int probe_highpoint45x(blkid_prob + uint32_t magic; + + if (pr->size < 0x10000) +- return -1; ++ return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) +- return -1; ++ return 1; + + off = ((pr->size / 0x200) - 11) * 0x200; + hpt = (struct hpt45x_metadata *) +@@ -40,13 +40,13 @@ static int probe_highpoint45x(blkid_prob + off, + sizeof(struct hpt45x_metadata)); + if (!hpt) +- return -1; ++ return errno ? -errno : 1; + magic = le32_to_cpu(hpt->magic); + if (magic != HPT45X_MAGIC_OK && magic != HPT45X_MAGIC_BAD) +- return -1; ++ return 1; + if (blkid_probe_set_magic(pr, off, sizeof(hpt->magic), + (unsigned char *) &hpt->magic)) +- return -1; ++ return 1; + return 0; + } + +@@ -54,7 +54,7 @@ static int probe_highpoint37x(blkid_prob + const struct blkid_idmag *mag __attribute__((__unused__))) + { + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) +- return -1; ++ return 1; + return 0; + } + +diff -up util-linux-2.23.2/libblkid/src/superblocks/hpfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/hpfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/hpfs.c.kzak 2013-04-08 17:55:40.252856159 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/hpfs.c 2014-03-28 15:11:44.680552016 +0100 +@@ -68,7 +68,7 @@ static int probe_hpfs(blkid_probe pr, co + /* super block */ + hs = blkid_probe_get_sb(pr, mag, struct hpfs_super_block); + if (!hs) +- return -1; ++ return errno ? -errno : 1; + version = hs->version; + + /* spare super block */ +@@ -77,9 +77,9 @@ static int probe_hpfs(blkid_probe pr, co + HPFS_SBSPARE_OFFSET, + sizeof(struct hpfs_spare_super)); + if (!hss) +- return -1; ++ return errno ? -errno : 1; + if (memcmp(hss->magic, "\x49\x18\x91\xf9", 4) != 0) +- return -1; ++ return 1; + + /* boot block (with UUID and LABEL) */ + hbb = (struct hpfs_boot_block *) +@@ -87,7 +87,7 @@ static int probe_hpfs(blkid_probe pr, co + 0, + sizeof(struct hpfs_boot_block)); + if (!hbb) +- return -1; ++ return errno ? -errno : 1; + if (memcmp(hbb->magic, "\x55\xaa", 2) == 0 && + memcmp(hbb->sig_hpfs, "HPFS", 4) == 0 && + hbb->sig_28h == 0x28) { +diff -up util-linux-2.23.2/libblkid/src/superblocks/iso9660.c.kzak util-linux-2.23.2/libblkid/src/superblocks/iso9660.c +--- util-linux-2.23.2/libblkid/src/superblocks/iso9660.c.kzak 2013-06-13 09:46:10.422650639 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/iso9660.c 2014-03-28 15:11:44.680552016 +0100 +@@ -100,7 +100,7 @@ static int probe_iso9660_hsfs(blkid_prob + + iso = blkid_probe_get_sb(pr, mag, struct high_sierra_volume_descriptor); + if (!iso) +- return -1; ++ return errno ? -errno : 1; + + blkid_probe_set_version(pr, "High Sierra"); + blkid_probe_set_label(pr, iso->volume_id, sizeof(iso->volume_id)); +@@ -178,7 +178,7 @@ int probe_iso9660(blkid_probe pr, const + + iso = blkid_probe_get_sb(pr, mag, struct iso_volume_descriptor); + if (!iso) +- return -1; ++ return errno ? -errno : 1; + + memcpy(label, iso->volume_id, sizeof(label)); + +diff -up util-linux-2.23.2/libblkid/src/superblocks/isw_raid.c.kzak util-linux-2.23.2/libblkid/src/superblocks/isw_raid.c +--- util-linux-2.23.2/libblkid/src/superblocks/isw_raid.c.kzak 2013-04-08 17:55:40.252856159 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/isw_raid.c 2014-03-28 15:11:44.680552016 +0100 +@@ -33,9 +33,9 @@ static int probe_iswraid(blkid_probe pr, + struct isw_metadata *isw; + + if (pr->size < 0x10000) +- return -1; ++ return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) +- return -1; ++ return 1; + + off = ((pr->size / 0x200) - 2) * 0x200; + isw = (struct isw_metadata *) +@@ -43,15 +43,16 @@ static int probe_iswraid(blkid_probe pr, + off, + sizeof(struct isw_metadata)); + if (!isw) +- return -1; ++ return errno ? -errno : 1; ++ + if (memcmp(isw->sig, ISW_SIGNATURE, sizeof(ISW_SIGNATURE)-1) != 0) +- return -1; ++ return 1; + if (blkid_probe_sprintf_version(pr, "%6s", + &isw->sig[sizeof(ISW_SIGNATURE)-1]) != 0) +- return -1; ++ return 1; + if (blkid_probe_set_magic(pr, off, sizeof(isw->sig), + (unsigned char *) isw->sig)) +- return -1; ++ return 1; + return 0; + } + +diff -up util-linux-2.23.2/libblkid/src/superblocks/jfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/jfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/jfs.c.kzak 2013-04-08 17:55:40.253856169 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/jfs.c 2014-03-28 15:11:44.681552026 +0100 +@@ -40,7 +40,7 @@ static int probe_jfs(blkid_probe pr, con + + js = blkid_probe_get_sb(pr, mag, struct jfs_super_block); + if (!js) +- return -1; ++ return errno ? -errno : 1; + if (le32_to_cpu(js->js_bsize) != (1U << le16_to_cpu(js->js_l2bsize))) + return 1; + if (le32_to_cpu(js->js_pbsize) != (1U << le16_to_cpu(js->js_l2pbsize))) +diff -up util-linux-2.23.2/libblkid/src/superblocks/jmicron_raid.c.kzak util-linux-2.23.2/libblkid/src/superblocks/jmicron_raid.c +--- util-linux-2.23.2/libblkid/src/superblocks/jmicron_raid.c.kzak 2013-04-08 17:55:40.253856169 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/jmicron_raid.c 2014-03-28 15:11:44.681552026 +0100 +@@ -32,9 +32,9 @@ static int probe_jmraid(blkid_probe pr, + struct jm_metadata *jm; + + if (pr->size < 0x10000) +- return -1; ++ return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) +- return -1; ++ return 1; + + off = ((pr->size / 0x200) - 1) * 0x200; + jm = (struct jm_metadata *) +@@ -42,15 +42,16 @@ static int probe_jmraid(blkid_probe pr, + off, + sizeof(struct jm_metadata)); + if (!jm) +- return -1; ++ return errno ? -errno : 1; ++ + if (memcmp(jm->signature, JM_SIGNATURE, sizeof(JM_SIGNATURE) - 1) != 0) +- return -1; ++ return 1; + if (blkid_probe_sprintf_version(pr, "%u.%u", + jm->major_version, jm->minor_version) != 0) +- return -1; ++ return 1; + if (blkid_probe_set_magic(pr, off, sizeof(jm->signature), + (unsigned char *) jm->signature)) +- return -1; ++ return 1; + return 0; + } + +diff -up util-linux-2.23.2/libblkid/src/superblocks/linux_raid.c.kzak util-linux-2.23.2/libblkid/src/superblocks/linux_raid.c +--- util-linux-2.23.2/libblkid/src/superblocks/linux_raid.c.kzak 2013-04-08 17:55:40.253856169 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/linux_raid.c 2014-03-28 15:11:44.681552026 +0100 +@@ -110,13 +110,13 @@ static int probe_raid0(blkid_probe pr, b + uint64_t size; + + if (pr->size < MD_RESERVED_BYTES) +- return -1; ++ return 1; + mdp0 = (struct mdp0_super_block *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct mdp0_super_block)); + if (!mdp0) +- return -1; ++ return errno ? -errno : 1; + + memset(uuid.ints, 0, sizeof(uuid.ints)); + +@@ -173,12 +173,12 @@ static int probe_raid0(blkid_probe pr, b + } + + if (blkid_probe_sprintf_version(pr, "%u.%u.%u", ma, mi, pa) != 0) +- return -1; ++ return 1; + if (blkid_probe_set_uuid(pr, (unsigned char *) uuid.bytes) != 0) +- return -1; ++ return 1; + if (blkid_probe_set_magic(pr, off, sizeof(mdp0->md_magic), + (unsigned char *) &mdp0->md_magic)) +- return -1; ++ return 1; + return 0; + } + +@@ -191,24 +191,24 @@ static int probe_raid1(blkid_probe pr, o + off, + sizeof(struct mdp1_super_block)); + if (!mdp1) +- return -1; ++ return errno ? -errno : 1; + if (le32_to_cpu(mdp1->magic) != MD_SB_MAGIC) +- return -1; ++ return 1; + if (le32_to_cpu(mdp1->major_version) != 1U) +- return -1; ++ return 1; + if (le64_to_cpu(mdp1->super_offset) != (uint64_t) off >> 9) +- return -1; ++ return 1; + if (blkid_probe_set_uuid(pr, (unsigned char *) mdp1->set_uuid) != 0) +- return -1; ++ return 1; + if (blkid_probe_set_uuid_as(pr, + (unsigned char *) mdp1->device_uuid, "UUID_SUB") != 0) +- return -1; ++ return 1; + if (blkid_probe_set_label(pr, mdp1->set_name, + sizeof(mdp1->set_name)) != 0) +- return -1; ++ return 1; + if (blkid_probe_set_magic(pr, off, sizeof(mdp1->magic), + (unsigned char *) &mdp1->magic)) +- return -1; ++ return 1; + return 0; + } + +@@ -216,35 +216,44 @@ int probe_raid(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) + { + const char *ver = NULL; ++ int ret = BLKID_PROBE_NONE; + + if (pr->size > MD_RESERVED_BYTES) { + /* version 0 at the end of the device */ + uint64_t sboff = (pr->size & ~(MD_RESERVED_BYTES - 1)) +- - MD_RESERVED_BYTES; +- if (probe_raid0(pr, sboff) == 0) +- return 0; ++ - MD_RESERVED_BYTES; ++ ret = probe_raid0(pr, sboff); ++ if (ret < 1) ++ return ret; /* error */ + + /* version 1.0 at the end of the device */ + sboff = (pr->size & ~(0x1000 - 1)) - 0x2000; +- if (probe_raid1(pr, sboff) == 0) ++ ret = probe_raid1(pr, sboff); ++ if (ret < 0) ++ return ret; /* error */ ++ if (ret == 0) + ver = "1.0"; + } + + if (!ver) { + /* version 1.1 at the start of the device */ +- if (probe_raid1(pr, 0) == 0) ++ ret = probe_raid1(pr, 0); ++ if (ret == 0) + ver = "1.1"; + + /* version 1.2 at 4k offset from the start */ +- else if (probe_raid1(pr, 0x1000) == 0) +- ver = "1.2"; ++ else if (ret == BLKID_PROBE_NONE) { ++ ret = probe_raid1(pr, 0x1000); ++ if (ret == 0) ++ ver = "1.2"; ++ } + } + + if (ver) { + blkid_probe_set_version(pr, ver); +- return 0; ++ return BLKID_PROBE_OK; + } +- return -1; ++ return ret; + } + + +diff -up util-linux-2.23.2/libblkid/src/superblocks/lsi_raid.c.kzak util-linux-2.23.2/libblkid/src/superblocks/lsi_raid.c +--- util-linux-2.23.2/libblkid/src/superblocks/lsi_raid.c.kzak 2013-04-08 17:55:40.254856179 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/lsi_raid.c 2014-03-28 15:11:44.681552026 +0100 +@@ -30,9 +30,9 @@ static int probe_lsiraid(blkid_probe pr, + struct lsi_metadata *lsi; + + if (pr->size < 0x10000) +- return -1; ++ return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) +- return -1; ++ return 1; + + off = ((pr->size / 0x200) - 1) * 0x200; + lsi = (struct lsi_metadata *) +@@ -40,13 +40,13 @@ static int probe_lsiraid(blkid_probe pr, + off, + sizeof(struct lsi_metadata)); + if (!lsi) +- return -1; ++ return errno ? -errno : 1; + + if (memcmp(lsi->sig, LSI_SIGNATURE, sizeof(LSI_SIGNATURE)-1) != 0) +- return -1; ++ return 1; + if (blkid_probe_set_magic(pr, off, sizeof(lsi->sig), + (unsigned char *) lsi->sig)) +- return -1; ++ return 1; + return 0; + } + +diff -up util-linux-2.23.2/libblkid/src/superblocks/luks.c.kzak util-linux-2.23.2/libblkid/src/superblocks/luks.c +--- util-linux-2.23.2/libblkid/src/superblocks/luks.c.kzak 2013-04-08 17:55:40.254856179 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/luks.c 2014-03-28 15:11:44.681552026 +0100 +@@ -45,7 +45,7 @@ static int probe_luks(blkid_probe pr, co + + header = blkid_probe_get_sb(pr, mag, struct luks_phdr); + if (header == NULL) +- return -1; ++ return errno ? -errno : 1; + + blkid_probe_strncpy_uuid(pr, (unsigned char *) header->uuid, + sizeof(header->uuid)); +diff -up util-linux-2.23.2/libblkid/src/superblocks/lvm.c.kzak util-linux-2.23.2/libblkid/src/superblocks/lvm.c +--- util-linux-2.23.2/libblkid/src/superblocks/lvm.c.kzak 2013-06-13 09:46:10.422650639 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/lvm.c 2014-03-28 15:11:44.681552026 +0100 +@@ -82,7 +82,7 @@ static int probe_lvm2(blkid_probe pr, co + mag->kboff << 10, + 512 + sizeof(struct lvm2_pv_label_header)); + if (!buf) +- return -1; ++ return errno ? -errno : 1; + + /* buf is at 0k or 1k offset; find label inside */ + if (memcmp(buf, "LABELONE", 8) == 0) { +@@ -129,7 +129,7 @@ static int probe_lvm1(blkid_probe pr, co + + label = blkid_probe_get_sb(pr, mag, struct lvm1_pv_label_header); + if (!label) +- return -1; ++ return errno ? -errno : 1; + + version = le16_to_cpu(label->version); + if (version != 1 && version != 2) +@@ -164,7 +164,7 @@ static int probe_verity(blkid_probe pr, + + sb = blkid_probe_get_sb(pr, mag, struct verity_sb); + if (sb == NULL) +- return -1; ++ return errno ? -errno : 1; + + version = le32_to_cpu(sb->version); + if (version != 1) +diff -up util-linux-2.23.2/libblkid/src/superblocks/minix.c.kzak util-linux-2.23.2/libblkid/src/superblocks/minix.c +--- util-linux-2.23.2/libblkid/src/superblocks/minix.c.kzak 2013-06-13 09:46:10.423650647 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/minix.c 2014-03-28 15:11:44.681552026 +0100 +@@ -80,17 +80,17 @@ static int probe_minix(blkid_probe pr, c + max(sizeof(struct minix_super_block), + sizeof(struct minix3_super_block))); + if (!data) +- return -1; ++ return errno ? -errno : 1; + version = get_minix_version(data, &swabme); + if (version < 1) +- return -1; ++ return 1; + + if (version <= 2) { + struct minix_super_block *sb = (struct minix_super_block *) data; + int zones, ninodes, imaps, zmaps, firstz; + + if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0) +- return -1; ++ return 1; + + zones = version == 2 ? minix_swab32(swabme, sb->s_zones) : + minix_swab16(swabme, sb->s_nzones); +@@ -101,15 +101,15 @@ static int probe_minix(blkid_probe pr, c + + /* sanity checks to be sure that the FS is really minix */ + if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1) +- return -1; ++ return 1; + if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1) +- return -1; ++ return 1; + + } else if (version == 3) { + struct minix3_super_block *sb = (struct minix3_super_block *) data; + + if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0) +- return -1; ++ return 1; + } + + /* unfortunately, some parts of ext3 is sometimes possible to +@@ -117,8 +117,10 @@ static int probe_minix(blkid_probe pr, c + * string. (For extN magic string and offsets see ext.c.) + */ + ext = blkid_probe_get_buffer(pr, 0x400 + 0x38, 2); +- if (ext && memcmp(ext, "\123\357", 2) == 0) +- return -1; ++ if (!ext) ++ return errno ? -errno : 1; ++ else if (memcmp(ext, "\123\357", 2) == 0) ++ return 1; + + blkid_probe_sprintf_version(pr, "%d", version); + return 0; +diff -up util-linux-2.23.2/libblkid/src/superblocks/netware.c.kzak util-linux-2.23.2/libblkid/src/superblocks/netware.c +--- util-linux-2.23.2/libblkid/src/superblocks/netware.c.kzak 2013-04-08 17:55:40.255856188 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/netware.c 2014-03-28 15:11:44.681552026 +0100 +@@ -71,7 +71,7 @@ static int probe_netware(blkid_probe pr, + + nw = blkid_probe_get_sb(pr, mag, struct netware_super_block); + if (!nw) +- return -1; ++ return errno ? -errno : 1; + + blkid_probe_set_uuid(pr, nw->SBH_PoolID); + +diff -up util-linux-2.23.2/libblkid/src/superblocks/nilfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/nilfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/nilfs.c.kzak 2013-04-08 17:55:40.256856198 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/nilfs.c 2014-03-28 15:11:44.681552026 +0100 +@@ -82,7 +82,7 @@ static int probe_nilfs2(blkid_probe pr, + + sb = blkid_probe_get_sb(pr, mag, struct nilfs_super_block); + if (!sb) +- return -1; ++ return errno ? -errno : 1; + + bytes = le16_to_cpu(sb->s_bytes); + crc = crc32(le32_to_cpu(sb->s_crc_seed), (unsigned char *)sb, sumoff); +@@ -90,7 +90,7 @@ static int probe_nilfs2(blkid_probe pr, + crc = crc32(crc, (unsigned char *)sb + sumoff + 4, bytes - sumoff - 4); + + if (crc != le32_to_cpu(sb->s_sum)) +- return -1; ++ return 1; + + if (strlen(sb->s_volume_name)) + blkid_probe_set_label(pr, (unsigned char *) sb->s_volume_name, +diff -up util-linux-2.23.2/libblkid/src/superblocks/ntfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/ntfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/ntfs.c.kzak 2013-06-13 09:46:10.423650647 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/ntfs.c 2014-03-28 15:11:44.681552026 +0100 +@@ -91,7 +91,7 @@ static int probe_ntfs(blkid_probe pr, co + + ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block); + if (!ns) +- return -1; ++ return errno ? -errno : 1; + + /* + * Check bios parameters block +@@ -158,7 +158,7 @@ static int probe_ntfs(blkid_probe pr, co + + buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); + if (!buf_mft) +- return 1; ++ return errno ? -errno : 1; + + if (memcmp(buf_mft, "FILE", 4)) + return 1; +@@ -167,7 +167,7 @@ static int probe_ntfs(blkid_probe pr, co + + buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); + if (!buf_mft) +- return 1; ++ return errno ? -errno : 1; + + if (memcmp(buf_mft, "FILE", 4)) + return 1; +diff -up util-linux-2.23.2/libblkid/src/superblocks/nvidia_raid.c.kzak util-linux-2.23.2/libblkid/src/superblocks/nvidia_raid.c +--- util-linux-2.23.2/libblkid/src/superblocks/nvidia_raid.c.kzak 2013-04-08 17:55:40.257856207 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/nvidia_raid.c 2014-03-28 15:11:44.682552036 +0100 +@@ -32,9 +32,9 @@ static int probe_nvraid(blkid_probe pr, + struct nv_metadata *nv; + + if (pr->size < 0x10000) +- return -1; ++ return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) +- return -1; ++ return 1; + + off = ((pr->size / 0x200) - 2) * 0x200; + nv = (struct nv_metadata *) +@@ -42,15 +42,15 @@ static int probe_nvraid(blkid_probe pr, + off, + sizeof(struct nv_metadata)); + if (!nv) +- return -1; ++ return errno ? -errno : 1; + + if (memcmp(nv->vendor, NVIDIA_SIGNATURE, sizeof(NVIDIA_SIGNATURE)-1) != 0) +- return -1; ++ return 1; + if (blkid_probe_sprintf_version(pr, "%u", le16_to_cpu(nv->version)) != 0) +- return -1; ++ return 1; + if (blkid_probe_set_magic(pr, off, sizeof(nv->vendor), + (unsigned char *) nv->vendor)) +- return -1; ++ return 1; + return 0; + } + +diff -up util-linux-2.23.2/libblkid/src/superblocks/ocfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/ocfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/ocfs.c.kzak 2013-04-08 17:55:40.257856207 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/ocfs.c 2014-03-28 15:11:44.682552036 +0100 +@@ -109,14 +109,14 @@ static int probe_ocfs(blkid_probe pr, co + buf = blkid_probe_get_buffer(pr, mag->kboff << 10, + sizeof(struct ocfs_volume_header)); + if (!buf) +- return -1; ++ return errno ? -errno : 1; + memcpy(&ovh, buf, sizeof(ovh)); + + /* label */ + buf = blkid_probe_get_buffer(pr, (mag->kboff << 10) + 512, + sizeof(struct ocfs_volume_label)); + if (!buf) +- return -1; ++ return errno ? -errno : 1; + memcpy(&ovl, buf, sizeof(ovl)); + + maj = ocfsmajor(ovh); +@@ -144,7 +144,7 @@ static int probe_ocfs2(blkid_probe pr, c + + osb = blkid_probe_get_sb(pr, mag, struct ocfs2_super_block); + if (!osb) +- return -1; ++ return errno ? -errno : 1; + + blkid_probe_set_label(pr, (unsigned char *) osb->s_label, sizeof(osb->s_label)); + blkid_probe_set_uuid(pr, osb->s_uuid); +@@ -162,7 +162,7 @@ static int probe_oracleasm(blkid_probe p + + dl = blkid_probe_get_sb(pr, mag, struct oracle_asm_disk_label); + if (!dl) +- return -1; ++ return errno ? -errno : 1; + + blkid_probe_set_label(pr, (unsigned char *) dl->dl_id, sizeof(dl->dl_id)); + return 0; +diff -up util-linux-2.23.2/libblkid/src/superblocks/promise_raid.c.kzak util-linux-2.23.2/libblkid/src/superblocks/promise_raid.c +--- util-linux-2.23.2/libblkid/src/superblocks/promise_raid.c.kzak 2013-06-13 09:46:10.423650647 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/promise_raid.c 2014-03-28 15:11:44.682552036 +0100 +@@ -33,9 +33,9 @@ static int probe_pdcraid(blkid_probe pr, + }; + + if (pr->size < 0x40000) +- return -1; ++ return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) +- return -1; ++ return 1; + + for (i = 0; sectors[i] != 0; i++) { + uint64_t off; +@@ -47,18 +47,18 @@ static int probe_pdcraid(blkid_probe pr, + off, + sizeof(struct promise_metadata)); + if (!pdc) +- return -1; ++ return errno ? -errno : 1; + + if (memcmp(pdc->sig, PDC_SIGNATURE, + sizeof(PDC_SIGNATURE) - 1) == 0) { + + if (blkid_probe_set_magic(pr, off, sizeof(pdc->sig), + (unsigned char *) pdc->sig)) +- return -1; ++ return 1; + return 0; + } + } +- return -1; ++ return 1; + } + + const struct blkid_idinfo pdcraid_idinfo = { +diff -up util-linux-2.23.2/libblkid/src/superblocks/reiserfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/reiserfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/reiserfs.c.kzak 2013-04-08 17:55:40.258856216 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/reiserfs.c 2014-03-28 15:11:44.682552036 +0100 +@@ -45,17 +45,17 @@ static int probe_reiser(blkid_probe pr, + + rs = blkid_probe_get_sb(pr, mag, struct reiserfs_super_block); + if (!rs) +- return -1; ++ return errno ? -errno : 1; + + blocksize = le16_to_cpu(rs->rs_blocksize); + + /* The blocksize must be at least 512B */ + if ((blocksize >> 9) == 0) +- return -BLKID_ERR_PARAM; ++ return 1; + + /* If the superblock is inside the journal, we have the wrong one */ + if (mag->kboff / (blocksize >> 9) > le32_to_cpu(rs->rs_journal_block) / 2) +- return -BLKID_ERR_BIG; ++ return 1; + + /* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */ + if (mag->magic[6] == '2' || mag->magic[6] == '3') { +@@ -82,7 +82,7 @@ static int probe_reiser4(blkid_probe pr, + + rs4 = blkid_probe_get_sb(pr, mag, struct reiser4_super_block); + if (!rs4) +- return -1; ++ return errno ? -errno : 1; + + if (*rs4->rs4_label) + blkid_probe_set_label(pr, rs4->rs4_label, sizeof(rs4->rs4_label)); +diff -up util-linux-2.23.2/libblkid/src/superblocks/romfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/romfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/romfs.c.kzak 2013-04-08 17:55:40.258856216 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/romfs.c 2014-03-28 15:11:44.682552036 +0100 +@@ -29,7 +29,7 @@ static int probe_romfs(blkid_probe pr, c + + ros = blkid_probe_get_sb(pr, mag, struct romfs_super_block); + if (!ros) +- return -1; ++ return errno ? -errno : 1; + + if (strlen((char *) ros->ros_volume)) + blkid_probe_set_label(pr, ros->ros_volume, +diff -up util-linux-2.23.2/libblkid/src/superblocks/silicon_raid.c.kzak util-linux-2.23.2/libblkid/src/superblocks/silicon_raid.c +--- util-linux-2.23.2/libblkid/src/superblocks/silicon_raid.c.kzak 2013-06-13 09:46:10.424650656 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/silicon_raid.c 2014-03-28 15:11:44.682552036 +0100 +@@ -88,9 +88,9 @@ static int probe_silraid(blkid_probe pr, + struct silicon_metadata *sil; + + if (pr->size < 0x10000) +- return -1; ++ return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) +- return -1; ++ return 1; + + off = ((pr->size / 0x200) - 1) * 0x200; + +@@ -98,27 +98,27 @@ static int probe_silraid(blkid_probe pr, + blkid_probe_get_buffer(pr, off, + sizeof(struct silicon_metadata)); + if (!sil) +- return -1; ++ return errno ? -errno : 1; + + if (le32_to_cpu(sil->magic) != SILICON_MAGIC) +- return -1; ++ return 1; + if (sil->disk_number >= 8) +- return -1; ++ return 1; + if (!checksum(sil)) { + DBG(LOWPROBE, blkid_debug("silicon raid: incorrect checksum")); +- return -1; ++ return 1; + } + + if (blkid_probe_sprintf_version(pr, "%u.%u", + le16_to_cpu(sil->major_ver), + le16_to_cpu(sil->minor_ver)) != 0) +- return -1; ++ return 1; + + if (blkid_probe_set_magic(pr, + off + offsetof(struct silicon_metadata, magic), + sizeof(sil->magic), + (unsigned char *) &sil->magic)) +- return -1; ++ return 1; + return 0; + } + +diff -up util-linux-2.23.2/libblkid/src/superblocks/squashfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/squashfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/squashfs.c.kzak 2013-04-08 17:55:40.258856216 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/squashfs.c 2014-03-28 15:11:44.682552036 +0100 +@@ -34,7 +34,7 @@ static int probe_squashfs(blkid_probe pr + + sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block); + if (!sq) +- return -1; ++ return errno ? -errno : 1; + + if (strcmp(mag->magic, "sqsh") == 0 || + strcmp(mag->magic, "qshs") == 0) +diff -up util-linux-2.23.2/libblkid/src/superblocks/superblocks.c.kzak util-linux-2.23.2/libblkid/src/superblocks/superblocks.c +--- util-linux-2.23.2/libblkid/src/superblocks/superblocks.c.kzak 2013-07-30 10:39:26.209738269 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/superblocks.c 2014-03-28 15:11:44.682552036 +0100 +@@ -331,9 +331,10 @@ int blkid_superblocks_get_name(size_t id + static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn) + { + size_t i; ++ int rc = BLKID_PROBE_NONE; + + if (!pr || chn->idx < -1) +- return -1; ++ return -EINVAL; + blkid_probe_chain_reset_vals(pr, chn); + + DBG(LOWPROBE, blkid_debug("--> starting probing loop [SUBLKS idx=%d]", +@@ -351,38 +352,50 @@ static int superblocks_probe(blkid_probe + const struct blkid_idinfo *id; + const struct blkid_idmag *mag = NULL; + blkid_loff_t off = 0; +- int rc = 0; + + chn->idx = i; + id = idinfos[i]; + + if (chn->fltr && blkid_bmp_get_item(chn->fltr, i)) { + DBG(LOWPROBE, blkid_debug("filter out: %s", id->name)); ++ rc = BLKID_PROBE_NONE; + continue; + } + +- if (id->minsz && id->minsz > pr->size) ++ if (id->minsz && id->minsz > pr->size) { ++ rc = BLKID_PROBE_NONE; + continue; /* the device is too small */ ++ } + + /* don't probe for RAIDs, swap or journal on CD/DVDs */ + if ((id->usage & (BLKID_USAGE_RAID | BLKID_USAGE_OTHER)) && +- blkid_probe_is_cdrom(pr)) ++ blkid_probe_is_cdrom(pr)) { ++ rc = BLKID_PROBE_NONE; + continue; ++ } + + /* don't probe for RAIDs on floppies */ +- if ((id->usage & BLKID_USAGE_RAID) && blkid_probe_is_tiny(pr)) ++ if ((id->usage & BLKID_USAGE_RAID) && blkid_probe_is_tiny(pr)) { ++ rc = BLKID_PROBE_NONE; + continue; ++ } + + DBG(LOWPROBE, blkid_debug("[%zd] %s:", i, id->name)); + +- if (blkid_probe_get_idmag(pr, id, &off, &mag)) ++ rc = blkid_probe_get_idmag(pr, id, &off, &mag); ++ if (rc < 0) ++ break; ++ if (rc != BLKID_PROBE_OK) + continue; + + /* final check by probing function */ + if (id->probefunc) { + DBG(LOWPROBE, blkid_debug("\tcall probefunc()")); +- if (id->probefunc(pr, mag) != 0) { ++ rc = id->probefunc(pr, mag); ++ if (rc != BLKID_PROBE_OK) { + blkid_probe_chain_reset_vals(pr, chn); ++ if (rc < 0) ++ break; + continue; + } + } +@@ -407,13 +420,13 @@ static int superblocks_probe(blkid_probe + + DBG(LOWPROBE, blkid_debug("<-- leaving probing loop (type=%s) [SUBLKS idx=%d]", + id->name, chn->idx)); +- return 0; ++ return BLKID_PROBE_OK; + } + + nothing: +- DBG(LOWPROBE, blkid_debug("<-- leaving probing loop (failed) [SUBLKS idx=%d]", +- chn->idx)); +- return 1; ++ DBG(LOWPROBE, blkid_debug("<-- leaving probing loop (failed=%d) [SUBLKS idx=%d]", ++ rc, chn->idx)); ++ return rc; + } + + /* +diff -up util-linux-2.23.2/libblkid/src/superblocks/swap.c.kzak util-linux-2.23.2/libblkid/src/superblocks/swap.c +--- util-linux-2.23.2/libblkid/src/superblocks/swap.c.kzak 2013-06-13 09:46:10.425650665 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/swap.c 2014-03-28 15:11:44.682552036 +0100 +@@ -44,17 +44,17 @@ static int swap_set_info(blkid_probe pr, + hdr = (struct swap_header_v1_2 *) blkid_probe_get_buffer(pr, 1024, + sizeof(struct swap_header_v1_2)); + if (!hdr) +- return -1; ++ return errno ? -errno : 1; + + /* SWAPSPACE2 - check for wrong version or zeroed pagecount */ + if (strcmp(version, "2") == 0) { + if (hdr->version != 1 && swab32(hdr->version) != 1) { + DBG(LOWPROBE, blkid_debug("incorrect swap version")); +- return -1; ++ return 1; + } + if (hdr->lastpage == 0) { + DBG(LOWPROBE, blkid_debug("not set last swap page")); +- return -1; ++ return 1; + } + } + +@@ -62,9 +62,9 @@ static int swap_set_info(blkid_probe pr, + if (hdr->padding[32] == 0 && hdr->padding[33] == 0) { + if (hdr->volume[0] && blkid_probe_set_label(pr, hdr->volume, + sizeof(hdr->volume)) < 0) +- return -1; ++ return 1; + if (blkid_probe_set_uuid(pr, hdr->uuid) < 0) +- return -1; ++ return 1; + } + + blkid_probe_set_version(pr, version); +@@ -76,12 +76,12 @@ static int probe_swap(blkid_probe pr, co + unsigned char *buf; + + if (!mag) +- return -1; ++ return 1; + + /* TuxOnIce keeps valid swap header at the end of the 1st page */ + buf = blkid_probe_get_buffer(pr, 0, TOI_MAGIC_STRLEN); + if (!buf) +- return -1; ++ return errno ? -errno : 1; + + if (memcmp(buf, TOI_MAGIC_STRING, TOI_MAGIC_STRLEN) == 0) + return 1; /* Ignore swap signature, it's TuxOnIce */ +@@ -94,13 +94,13 @@ static int probe_swap(blkid_probe pr, co + } else if (!memcmp(mag->magic, "SWAPSPACE2", mag->len)) + return swap_set_info(pr, "2"); + +- return -1; ++ return 1; + } + + static int probe_swsuspend(blkid_probe pr, const struct blkid_idmag *mag) + { + if (!mag) +- return -1; ++ return 1; + if (!memcmp(mag->magic, "S1SUSPEND", mag->len)) + return swap_set_info(pr, "s1suspend"); + if (!memcmp(mag->magic, "S2SUSPEND", mag->len)) +@@ -112,7 +112,7 @@ static int probe_swsuspend(blkid_probe p + if (!memcmp(mag->magic, "LINHIB0001", mag->len)) + return swap_set_info(pr, "linhib0001"); + +- return -1; /* no signature detected */ ++ return 1; /* no signature detected */ + } + + const struct blkid_idinfo swap_idinfo = +diff -up util-linux-2.23.2/libblkid/src/superblocks/sysv.c.kzak util-linux-2.23.2/libblkid/src/superblocks/sysv.c +--- util-linux-2.23.2/libblkid/src/superblocks/sysv.c.kzak 2013-04-08 17:55:40.261856245 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/sysv.c 2014-03-28 15:11:44.683552047 +0100 +@@ -80,7 +80,7 @@ static int probe_xenix(blkid_probe pr, c + + sb = blkid_probe_get_sb(pr, mag, struct xenix_super_block); + if (!sb) +- return -1; ++ return errno ? -errno : 1; + blkid_probe_set_label(pr, sb->s_fname, sizeof(sb->s_fname)); + return 0; + } +@@ -105,21 +105,21 @@ static int probe_sysv(blkid_probe pr, + off, + sizeof(struct sysv_super_block)); + if (!sb) +- return -1; ++ return errno ? -errno : 1; + + if (sb->s_magic == cpu_to_le32(0xfd187e20) || + sb->s_magic == cpu_to_be32(0xfd187e20)) { + + if (blkid_probe_set_label(pr, sb->s_fname, + sizeof(sb->s_fname))) +- return -1; ++ return 1; + + if (blkid_probe_set_magic(pr, + off + offsetof(struct sysv_super_block, + s_magic), + sizeof(sb->s_magic), + (unsigned char *) &sb->s_magic)) +- return -1; ++ return 1; + + return 0; + } +diff -up util-linux-2.23.2/libblkid/src/superblocks/ubifs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/ubifs.c +--- util-linux-2.23.2/libblkid/src/superblocks/ubifs.c.kzak 2013-06-13 09:46:10.426650673 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/ubifs.c 2014-03-28 15:11:44.683552047 +0100 +@@ -99,7 +99,7 @@ static int probe_ubifs(blkid_probe pr, c + + sb = blkid_probe_get_sb(pr, mag, struct ubifs_sb_node); + if (!sb) +- return -1; ++ return errno ? -errno : 1; + + blkid_probe_set_uuid(pr, sb->uuid); + blkid_probe_sprintf_version(pr, "w%dr%d", +diff -up util-linux-2.23.2/libblkid/src/superblocks/udf.c.kzak util-linux-2.23.2/libblkid/src/superblocks/udf.c +--- util-linux-2.23.2/libblkid/src/superblocks/udf.c.kzak 2013-06-13 09:46:10.426650673 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/udf.c 2014-03-28 15:11:44.683552047 +0100 +@@ -85,11 +85,11 @@ static int probe_udf(blkid_probe pr, + UDF_VSD_OFFSET + b, + sizeof(*vsd)); + if (!vsd) +- return 1; ++ return errno ? -errno : 1; + if (vsd->id[0] != '\0') + goto nsr; + } +- return -1; ++ return 1; + + nsr: + /* search the list of VSDs for a NSR descriptor */ +@@ -99,15 +99,15 @@ nsr: + UDF_VSD_OFFSET + ((blkid_loff_t) b * 0x800), + sizeof(*vsd)); + if (!vsd) +- return -1; ++ return errno ? -errno : 1; + if (vsd->id[0] == '\0') +- return -1; ++ return 1; + if (memcmp(vsd->id, "NSR02", 5) == 0) + goto anchor; + if (memcmp(vsd->id, "NSR03", 5) == 0) + goto anchor; + } +- return -1; ++ return 1; + + anchor: + /* read Anchor Volume Descriptor (AVDP), checking block size */ +@@ -115,7 +115,7 @@ anchor: + vd = (struct volume_descriptor *) + blkid_probe_get_buffer(pr, 256 * pbs[i], sizeof(*vd)); + if (!vd) +- return -1; ++ return errno ? -errno : 1; + + type = le16_to_cpu(vd->tag.id); + if (type == 2) /* TAG_ID_AVDP */ +@@ -138,7 +138,7 @@ real_blksz: + (blkid_loff_t) (loc + b) * bs, + sizeof(*vd)); + if (!vd) +- return -1; ++ return errno ? -errno : 1; + } + + /* Try extract all possible ISO9660 information -- if there is +@@ -155,7 +155,7 @@ real_blksz: + (blkid_loff_t) (loc + b) * bs, + sizeof(*vd)); + if (!vd) +- return -1; ++ return errno ? -errno : 1; + type = le16_to_cpu(vd->tag.id); + if (type == 0) + break; +diff -up util-linux-2.23.2/libblkid/src/superblocks/ufs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/ufs.c +--- util-linux-2.23.2/libblkid/src/superblocks/ufs.c.kzak 2013-04-08 17:55:40.263856264 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/ufs.c 2014-03-28 15:11:44.683552047 +0100 +@@ -185,7 +185,7 @@ static int probe_ufs(blkid_probe pr, + offsets[i] * 1024, + sizeof(struct ufs_super_block)); + if (!ufs) +- return -1; ++ return errno ? -errno : 1; + + magBE = be32_to_cpu(ufs->fs_magic); + magLE = le32_to_cpu(ufs->fs_magic); +@@ -231,7 +231,7 @@ found: + offsetof(struct ufs_super_block, fs_magic), + sizeof(ufs->fs_magic), + (unsigned char *) &ufs->fs_magic)) +- return -1; ++ return 1; + + return 0; + } +diff -up util-linux-2.23.2/libblkid/src/superblocks/vfat.c.kzak util-linux-2.23.2/libblkid/src/superblocks/vfat.c +--- util-linux-2.23.2/libblkid/src/superblocks/vfat.c.kzak 2013-06-13 09:46:10.426650673 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/vfat.c 2014-03-28 15:12:28.036993622 +0100 +@@ -255,16 +255,20 @@ int blkid_probe_is_vfat(blkid_probe pr) + struct vfat_super_block *vs; + struct msdos_super_block *ms; + const struct blkid_idmag *mag = NULL; ++ int rc; + +- if (blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag) || !mag) ++ rc = blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag); ++ if (rc < 0) ++ return rc; /* error */ ++ if (rc != BLKID_PROBE_OK || !mag) + return 0; + + ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block); + if (!ms) +- return 0; ++ return errno ? -errno : 0; + vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block); + if (!vs) +- return 0; ++ return errno ? -errno : 0; + + return fat_valid_superblock(mag, ms, vs, NULL, NULL); + } +@@ -283,10 +287,12 @@ static int probe_vfat(blkid_probe pr, co + + ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block); + if (!ms) +- return 0; ++ return errno ? -errno : 1; ++ + vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block); + if (!vs) +- return 0; ++ return errno ? -errno : 1; ++ + if (!fat_valid_superblock(mag, ms, vs, &cluster_count, &fat_size)) + return 1; + +@@ -376,16 +382,16 @@ static int probe_vfat(blkid_probe pr, co + (blkid_loff_t) fsinfo_sect * sector_size, + sizeof(struct fat32_fsinfo)); + if (buf == NULL) +- return -1; ++ return errno ? -errno : 1; + + fsinfo = (struct fat32_fsinfo *) buf; + if (memcmp(fsinfo->signature1, "\x52\x52\x61\x41", 4) != 0 && + memcmp(fsinfo->signature1, "\x52\x52\x64\x41", 4) != 0 && + memcmp(fsinfo->signature1, "\x00\x00\x00\x00", 4) != 0) +- return -1; ++ return 1; + if (memcmp(fsinfo->signature2, "\x72\x72\x41\x61", 4) != 0 && + memcmp(fsinfo->signature2, "\x00\x00\x00\x00", 4) != 0) +- return -1; ++ return 1; + } + } + +diff -up util-linux-2.23.2/libblkid/src/superblocks/via_raid.c.kzak util-linux-2.23.2/libblkid/src/superblocks/via_raid.c +--- util-linux-2.23.2/libblkid/src/superblocks/via_raid.c.kzak 2013-04-08 17:55:40.264856273 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/via_raid.c 2014-03-28 15:11:44.683552047 +0100 +@@ -52,9 +52,9 @@ static int probe_viaraid(blkid_probe pr, + struct via_metadata *v; + + if (pr->size < 0x10000) +- return -1; ++ return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) +- return -1; ++ return 1; + + off = ((pr->size / 0x200)-1) * 0x200; + +@@ -63,19 +63,19 @@ static int probe_viaraid(blkid_probe pr, + off, + sizeof(struct via_metadata)); + if (!v) +- return -1; ++ return errno ? -errno : 1; + if (le16_to_cpu(v->signature) != VIA_SIGNATURE) +- return -1; ++ return 1; + if (v->version_number > 2) +- return -1; ++ return 1; + if (!via_checksum(v)) +- return -1; ++ return 1; + if (blkid_probe_sprintf_version(pr, "%u", v->version_number) != 0) +- return -1; ++ return 1; + if (blkid_probe_set_magic(pr, off, + sizeof(v->signature), + (unsigned char *) &v->signature)) +- return -1; ++ return 1; + return 0; + } + +diff -up util-linux-2.23.2/libblkid/src/superblocks/vmfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/vmfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/vmfs.c.kzak 2013-04-08 17:55:40.264856273 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/vmfs.c 2014-03-28 15:11:44.683552047 +0100 +@@ -28,7 +28,7 @@ static int probe_vmfs_fs(blkid_probe pr, + + header = blkid_probe_get_sb(pr, mag, struct vmfs_fs_info); + if (header == NULL) +- return -1; ++ return errno ? -errno : 1; + + blkid_probe_sprintf_uuid(pr, (unsigned char *) header->uuid, 16, + "%02x%02x%02x%02x-%02x%02x%02x%02x-" +@@ -53,7 +53,7 @@ static int probe_vmfs_volume(blkid_probe + + header = blkid_probe_get_sb(pr, mag, struct vmfs_volume_info); + if (header == NULL) +- return -1; ++ return errno ? -errno : 1; + + blkid_probe_sprintf_value(pr, "UUID_SUB", + "%02x%02x%02x%02x-%02x%02x%02x%02x-" +diff -up util-linux-2.23.2/libblkid/src/superblocks/vxfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/vxfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/vxfs.c.kzak 2013-04-08 17:55:40.264856273 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/vxfs.c 2014-03-28 15:11:44.683552047 +0100 +@@ -20,7 +20,7 @@ static int probe_vxfs(blkid_probe pr, co + + vxs = blkid_probe_get_sb(pr, mag, struct vxfs_super_block); + if (!vxs) +- return -1; ++ return errno ? -errno : 1; + + blkid_probe_sprintf_version(pr, "%u", (unsigned int) vxs->vs_version); + return 0; +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 2013-04-08 17:55:40.265856283 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/xfs.c 2014-03-28 15:11:44.683552047 +0100 +@@ -40,7 +40,7 @@ static int probe_xfs(blkid_probe pr, con + + xs = blkid_probe_get_sb(pr, mag, struct xfs_super_block); + if (!xs) +- return -1; ++ return errno ? -errno : 1; + + if (strlen(xs->xs_fname)) + blkid_probe_set_label(pr, (unsigned char *) xs->xs_fname, +diff -up util-linux-2.23.2/libblkid/src/superblocks/zfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/zfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/zfs.c.kzak 2013-06-13 09:46:10.427650682 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/zfs.c 2014-03-28 15:11:44.683552047 +0100 +@@ -185,7 +185,7 @@ static int probe_zfs(blkid_probe pr, + blkid_probe_get_buffer(pr, offset, + sizeof(struct zfs_uberblock)); + if (ub == NULL) +- return -1; ++ return errno ? -errno : 1; + + if (ub->ub_magic == UBERBLOCK_MAGIC) { + ub_offset = offset; +@@ -202,7 +202,7 @@ static int probe_zfs(blkid_probe pr, + } + + if (found < 4) +- return -1; ++ return 1; + + /* If we found the 4th uberblock, then we will have exited from the + * scanning loop immediately, and ub will be a valid uberblock. */ +@@ -214,7 +214,7 @@ static int probe_zfs(blkid_probe pr, + if (blkid_probe_set_magic(pr, ub_offset, + sizeof(ub->ub_magic), + (unsigned char *) &ub->ub_magic)) +- return -1; ++ return 1; + + return 0; + } diff --git a/SOURCES/2.25-libblkid-no-more-probe-for-btrfs-backup-superblock.patch b/SOURCES/2.25-libblkid-no-more-probe-for-btrfs-backup-superblock.patch new file mode 100644 index 0000000..5083b38 --- /dev/null +++ b/SOURCES/2.25-libblkid-no-more-probe-for-btrfs-backup-superblock.patch @@ -0,0 +1,68 @@ +diff -up util-linux-2.23.2/libblkid/src/blkidP.h.kzak util-linux-2.23.2/libblkid/src/blkidP.h +--- util-linux-2.23.2/libblkid/src/blkidP.h.kzak 2013-07-30 10:39:26.205738229 +0200 ++++ util-linux-2.23.2/libblkid/src/blkidP.h 2014-01-23 10:51:10.109593273 +0100 +@@ -224,9 +224,6 @@ struct blkid_struct_probe + + /* private per-probing flags */ + #define BLKID_PROBE_FL_IGNORE_PT (1 << 1) /* ignore partition table */ +-#define BLKID_PROBE_FL_IGNORE_BACKUP (1 << 2) /* ignore backup superblocks or PT */ +- +-extern int blkid_probe_ignore_backup(blkid_probe pr); + + extern blkid_probe blkid_clone_probe(blkid_probe parent); + extern blkid_probe blkid_probe_get_wholedisk_probe(blkid_probe pr); +diff -up util-linux-2.23.2/libblkid/src/probe.c.kzak util-linux-2.23.2/libblkid/src/probe.c +--- util-linux-2.23.2/libblkid/src/probe.c.kzak 2013-07-30 10:39:26.208738259 +0200 ++++ util-linux-2.23.2/libblkid/src/probe.c 2014-01-23 10:51:10.109593273 +0100 +@@ -924,7 +924,8 @@ int blkid_do_probe(blkid_probe pr) + * + * This function erases the current signature detected by @pr. The @pr has to + * be open in O_RDWR mode, BLKID_SUBLKS_MAGIC or/and BLKID_PARTS_MAGIC flags +- * has to be enabled. ++ * has to be enabled (if you want to errase also superblock with broken check ++ * sums then use BLKID_SUBLKS_BADCSUM too). + * + * After successful signature removing the @pr prober will be moved one step + * back and the next blkid_do_probe() call will again call previously called +@@ -1125,8 +1126,6 @@ int blkid_do_safeprobe(blkid_probe pr) + + blkid_probe_start(pr); + +- pr->prob_flags |= BLKID_PROBE_FL_IGNORE_BACKUP; +- + for (i = 0; i < BLKID_NCHAINS; i++) { + struct blkid_chain *chn; + +@@ -1764,8 +1763,3 @@ void blkid_probe_use_wiper(blkid_probe p + blkid_probe_chain_reset_vals(pr, chn); + } + } +- +-int blkid_probe_ignore_backup(blkid_probe pr) +-{ +- return pr && (pr->prob_flags & BLKID_PROBE_FL_IGNORE_BACKUP); +-} +diff -up util-linux-2.23.2/libblkid/src/superblocks/btrfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/btrfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/btrfs.c.kzak 2013-06-13 09:46:10.421650630 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/btrfs.c 2014-01-23 10:51:10.109593273 +0100 +@@ -63,11 +63,6 @@ static int probe_btrfs(blkid_probe pr, c + { + struct btrfs_super_block *bfs; + +- if (mag->kboff > 64 && blkid_probe_ignore_backup(pr)) { +- DBG(LOWPROBE, blkid_debug("btrfs: found backup superblock, ignore")); +- return 1; +- } +- + bfs = blkid_probe_get_sb(pr, mag, struct btrfs_super_block); + if (!bfs) + return -1; +@@ -92,8 +87,6 @@ const struct blkid_idinfo btrfs_idinfo = + .magics = + { + { .magic = "_BHRfS_M", .len = 8, .sboff = 0x40, .kboff = 64 }, +- { .magic = "_BHRfS_M", .len = 8, .sboff = 0x40, .kboff = 64 * 1024 }, +- { .magic = "_BHRfS_M", .len = 8, .sboff = 0x40, .kboff = 256 * 1024 * 1024 }, + { NULL } + } + }; diff --git a/SOURCES/2.25-libblkid-return-codes.patch b/SOURCES/2.25-libblkid-return-codes.patch new file mode 100644 index 0000000..6462359 --- /dev/null +++ b/SOURCES/2.25-libblkid-return-codes.patch @@ -0,0 +1,204 @@ +diff -up util-linux-2.23.2/libblkid/src/partitions/partitions.c.kzak util-linux-2.23.2/libblkid/src/partitions/partitions.c +--- util-linux-2.23.2/libblkid/src/partitions/partitions.c.kzak 2015-08-11 14:00:27.930573965 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/partitions.c 2015-08-11 14:13:11.213087814 +0200 +@@ -533,7 +533,7 @@ static int idinfo_probe(blkid_probe pr, + { + const struct blkid_idmag *mag = NULL; + blkid_loff_t off; +- int rc = BLKID_PROBE_NONE; /* = nothing detected */ ++ int rc = BLKID_PROBE_NONE; /* default is nothing */ + + if (pr->size <= 0 || (id->minsz && id->minsz > pr->size)) + goto nothing; /* the device is too small */ +@@ -564,8 +564,10 @@ static int idinfo_probe(blkid_probe pr, + DBG(LOWPROBE, blkid_debug("%s: <--- (rc = %d)", id->name, rc)); + } + +-nothing: + return rc; ++ ++nothing: ++ return BLKID_PROBE_NONE; + } + + /* +@@ -620,7 +622,7 @@ static int partitions_probe(blkid_probe + strlen(name) + 1); + DBG(LOWPROBE, blkid_debug("<-- leaving probing loop (type=%s) [PARTS idx=%d]", + name, chn->idx)); +- rc = 0; ++ rc = BLKID_PROBE_OK; + break; + } + +@@ -637,10 +639,16 @@ details_only: + (blkid_partitions_get_flags(pr) & BLKID_PARTS_ENTRY_DETAILS)) { + + int xrc = blkid_partitions_probe_partition(pr); ++ ++ /* partition entry probing is optional, and "not-found" from ++ * this sub-probing must not to overwrite previous success. */ + if (xrc < 0) +- rc = xrc; /* optional, care about errors only */ ++ rc = xrc; /* always propagate errors */ ++ else if (rc == BLKID_PROBE_NONE) ++ rc = xrc; + } + ++ DBG(LOWPROBE, blkid_debug("partitions probe done [rc=%d]", rc)); + return rc; + } + +@@ -709,7 +717,6 @@ int blkid_partitions_do_subprobe(blkid_p + + static int blkid_partitions_probe_partition(blkid_probe pr) + { +- int rc = BLKID_PROBE_NONE; + blkid_probe disk_pr = NULL; + blkid_partlist ls; + blkid_partition par; +@@ -732,7 +739,9 @@ static int blkid_partitions_probe_partit + goto nothing; + + par = blkid_partlist_devno_to_partition(ls, devno); +- if (par) { ++ if (!par) ++ goto nothing; ++ else { + const char *v; + blkid_parttable tab = blkid_partition_get_table(par); + dev_t disk = blkid_probe_get_devno(disk_pr); +@@ -778,9 +787,13 @@ static int blkid_partitions_probe_partit + blkid_probe_sprintf_value(pr, "PART_ENTRY_DISK", "%u:%u", + major(disk), minor(disk)); + } +- rc = BLKID_PROBE_OK; ++ ++ DBG(LOWPROBE, blkid_debug("parts: end probing for partition entry [success]")); ++ return BLKID_PROBE_OK; ++ + nothing: +- return rc; ++ DBG(LOWPROBE, blkid_debug("parts: end probing for partition entry [nothing]")); ++ return BLKID_PROBE_NONE; + } + + /* +diff -up util-linux-2.23.2/libblkid/src/superblocks/superblocks.c.kzak util-linux-2.23.2/libblkid/src/superblocks/superblocks.c +--- util-linux-2.23.2/libblkid/src/superblocks/superblocks.c.kzak 2015-07-03 10:35:05.456153560 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/superblocks.c 2015-08-11 14:08:42.572674469 +0200 +@@ -335,19 +335,24 @@ static int superblocks_probe(blkid_probe + + if (!pr || chn->idx < -1) + return -EINVAL; +- if (pr->flags & BLKID_FL_NOSCAN_DEV) +- goto nothing; + + blkid_probe_chain_reset_vals(pr, chn); + + DBG(LOWPROBE, blkid_debug("--> starting probing loop [SUBLKS idx=%d]", + chn->idx)); + ++ if (pr->flags & BLKID_FL_NOSCAN_DEV) ++ return BLKID_PROBE_NONE; ++ + if (pr->size <= 0 || (pr->size <= 1024 && !S_ISCHR(pr->mode))) + /* Ignore very very small block devices or regular files (e.g. + * extended partitions). Note that size of the UBI char devices + * is 1 byte */ +- goto nothing; ++ return BLKID_PROBE_NONE; ++ ++ ++ DBG(LOWPROBE, blkid_debug("--> starting probing loop [SUBLKS idx=%d]", ++ chn->idx)); + + i = chn->idx < 0 ? 0 : chn->idx + 1U; + +@@ -417,7 +422,7 @@ static int superblocks_probe(blkid_probe + (unsigned char *) mag->magic); + if (rc) { + blkid_probe_chain_reset_vals(pr, chn); +- DBG(LOWPROBE, blkid_debug("failed to set result -- ingnore")); ++ DBG(LOWPROBE, blkid_debug("failed to set result -- ignore")); + continue; + } + +@@ -426,7 +431,6 @@ static int superblocks_probe(blkid_probe + return BLKID_PROBE_OK; + } + +-nothing: + DBG(LOWPROBE, blkid_debug("<-- leaving probing loop (failed=%d) [SUBLKS idx=%d]", + rc, chn->idx)); + return rc; +@@ -454,13 +458,12 @@ static int superblocks_safeprobe(blkid_p + int rc; + + if (pr->flags & BLKID_FL_NOSCAN_DEV) +- return 1; /* nothing */ ++ return BLKID_PROBE_NONE; + + while ((rc = superblocks_probe(pr, chn)) == 0) { + + if (blkid_probe_is_tiny(pr) && !count) +- /* floppy or so -- returns the first result. */ +- return 0; ++ return BLKID_PROBE_OK; /* floppy or so -- returns the first result. */ + + count++; + +@@ -489,7 +492,7 @@ static int superblocks_safeprobe(blkid_p + return -2; /* error, ambivalent result (more FS) */ + } + if (!count) +- return 1; /* nothing detected */ ++ return BLKID_PROBE_NONE; + + if (idx != -1) { + /* restore the first result */ +@@ -506,7 +509,7 @@ static int superblocks_safeprobe(blkid_p + if (chn->idx >= 0 && idinfos[chn->idx]->usage & BLKID_USAGE_RAID) + pr->prob_flags |= BLKID_PROBE_FL_IGNORE_PT; + +- return 0; ++ return BLKID_PROBE_OK; + } + + int blkid_probe_set_version(blkid_probe pr, const char *version) +diff -up util-linux-2.23.2/libblkid/src/topology/topology.c.kzak util-linux-2.23.2/libblkid/src/topology/topology.c +--- util-linux-2.23.2/libblkid/src/topology/topology.c.kzak 2013-06-13 09:46:10.429650699 +0200 ++++ util-linux-2.23.2/libblkid/src/topology/topology.c 2015-08-11 14:09:35.623361499 +0200 +@@ -150,7 +150,7 @@ static int topology_probe(blkid_probe pr + return -1; + + if (!S_ISBLK(pr->mode)) +- return -1; /* nothing, works with block devices only */ ++ return -EINVAL; /* nothing, works with block devices only */ + + if (chn->binary) { + DBG(LOWPROBE, blkid_debug("initialize topology binary data")); +@@ -163,7 +163,7 @@ static int topology_probe(blkid_probe pr + chn->data = calloc(1, + sizeof(struct blkid_struct_topology)); + if (!chn->data) +- return -1; ++ return -ENOMEM; + } + } + +@@ -193,12 +193,12 @@ static int topology_probe(blkid_probe pr + + DBG(LOWPROBE, blkid_debug("<-- leaving probing loop (type=%s) [TOPOLOGY idx=%d]", + id->name, chn->idx)); +- return 0; ++ return BLKID_PROBE_OK; + } + + DBG(LOWPROBE, blkid_debug("<-- leaving probing loop (failed) [TOPOLOGY idx=%d]", + chn->idx)); +- return 1; ++ return BLKID_PROBE_NONE; + } + + static void topology_free(blkid_probe pr __attribute__((__unused__)), diff --git a/SOURCES/2.25-libblkid-thinpool.patch b/SOURCES/2.25-libblkid-thinpool.patch new file mode 100644 index 0000000..b17b98c --- /dev/null +++ b/SOURCES/2.25-libblkid-thinpool.patch @@ -0,0 +1,237 @@ +diff -up util-linux-2.23.2/fdisks/fdisk.c.kzak util-linux-2.23.2/fdisks/fdisk.c +--- util-linux-2.23.2/fdisks/fdisk.c.kzak 2015-07-02 12:37:24.465906322 +0200 ++++ util-linux-2.23.2/fdisks/fdisk.c 2015-07-02 12:37:57.870673753 +0200 +@@ -34,6 +34,7 @@ + #include "canonicalize.h" + #include "strutils.h" + #include "closestream.h" ++#include "sysfs.h" + + #include "fdisksunlabel.h" + #include "fdisksgilabel.h" +diff -up util-linux-2.23.2/include/sysfs.h.kzak util-linux-2.23.2/include/sysfs.h +--- util-linux-2.23.2/include/sysfs.h.kzak 2015-07-02 12:12:50.408196320 +0200 ++++ util-linux-2.23.2/include/sysfs.h 2015-07-02 12:13:09.708061372 +0200 +@@ -74,6 +74,8 @@ extern int sysfs_devno_to_wholedisk(dev_ + size_t len, dev_t *diskdevno); + extern int sysfs_devno_is_wholedisk(dev_t devno); + ++extern int sysfs_devno_is_lvm_private(dev_t devno); ++ + extern int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, + int *c, int *t, int *l); + extern char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt, +diff -up util-linux-2.23.2/libblkid/src/blkidP.h.kzak util-linux-2.23.2/libblkid/src/blkidP.h +--- util-linux-2.23.2/libblkid/src/blkidP.h.kzak 2015-07-02 12:18:27.349840375 +0200 ++++ util-linux-2.23.2/libblkid/src/blkidP.h 2015-07-02 12:19:07.797557558 +0200 +@@ -221,6 +221,7 @@ struct blkid_struct_probe + #define BLKID_FL_PRIVATE_FD (1 << 1) /* see blkid_new_probe_from_filename() */ + #define BLKID_FL_TINY_DEV (1 << 2) /* <= 1.47MiB (floppy or so) */ + #define BLKID_FL_CDROM_DEV (1 << 3) /* is a CD/DVD drive */ ++#define BLKID_FL_NOSCAN_DEV (1 << 4) /* do not scan this device */ + + /* private per-probing flags */ + #define BLKID_PROBE_FL_IGNORE_PT (1 << 1) /* ignore partition table */ +diff -up util-linux-2.23.2/libblkid/src/partitions/partitions.c.kzak util-linux-2.23.2/libblkid/src/partitions/partitions.c +--- util-linux-2.23.2/libblkid/src/partitions/partitions.c.kzak 2015-07-02 12:19:11.669530485 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/partitions.c 2015-07-02 12:28:24.166667964 +0200 +@@ -537,6 +537,8 @@ static int idinfo_probe(blkid_probe pr, + + if (pr->size <= 0 || (id->minsz && id->minsz > pr->size)) + goto nothing; /* the device is too small */ ++ if (pr->flags & BLKID_FL_NOSCAN_DEV) ++ goto nothing; + + rc = blkid_probe_get_idmag(pr, id, &off, &mag); + if (rc != BLKID_PROBE_OK) +@@ -576,8 +578,12 @@ static int partitions_probe(blkid_probe + + if (!pr || chn->idx < -1) + return -EINVAL; ++ + blkid_probe_chain_reset_vals(pr, chn); + ++ if (pr->flags & BLKID_FL_NOSCAN_DEV) ++ return BLKID_PROBE_NONE; ++ + if (chn->binary) + partitions_init_data(chn); + +@@ -653,6 +659,8 @@ int blkid_partitions_do_subprobe(blkid_p + + if (!pr || !parent || !parent->size) + return -EINVAL; ++ if (pr->flags & BLKID_FL_NOSCAN_DEV) ++ return BLKID_PROBE_NONE; + + /* range defined by parent */ + sz = ((blkid_loff_t) parent->size) << 9; +@@ -707,6 +715,9 @@ static int blkid_partitions_probe_partit + blkid_partition par; + dev_t devno; + ++ if (pr->flags & BLKID_FL_NOSCAN_DEV) ++ goto nothing; ++ + devno = blkid_probe_get_devno(pr); + if (!devno) + goto nothing; +@@ -779,7 +790,7 @@ nothing: + int blkid_probe_is_covered_by_pt(blkid_probe pr, + blkid_loff_t offset, blkid_loff_t size) + { +- blkid_probe prc; ++ blkid_probe prc = NULL; + blkid_partlist ls = NULL; + blkid_loff_t start, end; + int nparts, i, rc = 0; +@@ -788,6 +799,9 @@ int blkid_probe_is_covered_by_pt(blkid_p + "=> checking if off=%jd size=%jd covered by PT", + offset, size)); + ++ if (pr->flags & BLKID_FL_NOSCAN_DEV) ++ goto done; ++ + prc = blkid_clone_probe(pr); + if (!prc) + goto done; +diff -up util-linux-2.23.2/libblkid/src/probe.c.kzak util-linux-2.23.2/libblkid/src/probe.c +--- util-linux-2.23.2/libblkid/src/probe.c.kzak 2015-07-02 12:13:48.823787869 +0200 ++++ util-linux-2.23.2/libblkid/src/probe.c 2015-07-02 12:38:20.110518915 +0200 +@@ -110,6 +110,7 @@ + + #include "blkidP.h" + #include "all-io.h" ++#include "sysfs.h" + + /* chains */ + extern const struct blkid_chaindrv superblocks_drv; +@@ -714,8 +715,13 @@ int blkid_probe_set_device(blkid_probe p + if (pr->size <= 1440 * 1024 && !S_ISCHR(sb.st_mode)) + pr->flags |= BLKID_FL_TINY_DEV; + ++ if (S_ISBLK(sb.st_mode) && sysfs_devno_is_lvm_private(sb.st_rdev)) { ++ DBG(LOWPROBE, blkid_debug("ignore private LVM device")); ++ pr->flags |= BLKID_FL_NOSCAN_DEV; ++ } ++ + #ifdef CDROM_GET_CAPABILITY +- if (S_ISBLK(sb.st_mode) && ++ else if (S_ISBLK(sb.st_mode) && + !blkid_probe_is_tiny(pr) && + blkid_probe_is_wholedisk(pr) && + ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0) +@@ -892,6 +898,9 @@ int blkid_do_probe(blkid_probe pr) + if (!pr) + return -1; + ++ if (pr->flags & BLKID_FL_NOSCAN_DEV) ++ return 1; ++ + do { + struct blkid_chain *chn = pr->cur_chain; + +@@ -1143,6 +1152,8 @@ int blkid_do_safeprobe(blkid_probe pr) + + if (!pr) + return -1; ++ if (pr->flags & BLKID_FL_NOSCAN_DEV) ++ return 1; + + blkid_probe_start(pr); + +@@ -1197,6 +1208,8 @@ int blkid_do_fullprobe(blkid_probe pr) + + if (!pr) + return -1; ++ if (pr->flags & BLKID_FL_NOSCAN_DEV) ++ return 1; + + blkid_probe_start(pr); + +diff -up util-linux-2.23.2/libblkid/src/superblocks/superblocks.c.kzak util-linux-2.23.2/libblkid/src/superblocks/superblocks.c +--- util-linux-2.23.2/libblkid/src/superblocks/superblocks.c.kzak 2015-07-02 12:29:32.370193121 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/superblocks.c 2015-07-02 12:31:06.897535008 +0200 +@@ -338,6 +338,9 @@ static int superblocks_probe(blkid_probe + + if (!pr || chn->idx < -1) + return -EINVAL; ++ if (pr->flags & BLKID_FL_NOSCAN_DEV) ++ goto nothing; ++ + blkid_probe_chain_reset_vals(pr, chn); + + DBG(LOWPROBE, blkid_debug("--> starting probing loop [SUBLKS idx=%d]", +@@ -453,6 +456,9 @@ static int superblocks_safeprobe(blkid_p + int intol = 0; + int rc; + ++ if (pr->flags & BLKID_FL_NOSCAN_DEV) ++ return 1; /* nothing */ ++ + while ((rc = superblocks_probe(pr, chn)) == 0) { + + if (blkid_probe_is_tiny(pr) && !count) +diff -up util-linux-2.23.2/libblkid/src/verify.c.kzak util-linux-2.23.2/libblkid/src/verify.c +--- util-linux-2.23.2/libblkid/src/verify.c.kzak 2015-07-02 12:15:51.782928121 +0200 ++++ util-linux-2.23.2/libblkid/src/verify.c 2015-07-02 12:16:45.078555470 +0200 +@@ -112,6 +112,10 @@ blkid_dev blkid_verify(blkid_cache cache + (unsigned long)diff)); + #endif + ++ if (sysfs_devno_is_lvm_private(st.st_rdev)) { ++ blkid_free_dev(dev); ++ return NULL; ++ } + if (!cache->probe) { + cache->probe = blkid_new_probe(); + if (!cache->probe) { +diff -up util-linux-2.23.2/lib/sysfs.c.kzak util-linux-2.23.2/lib/sysfs.c +--- util-linux-2.23.2/lib/sysfs.c.kzak 2015-07-02 12:12:29.193344657 +0200 ++++ util-linux-2.23.2/lib/sysfs.c 2015-07-02 12:12:13.565453930 +0200 +@@ -639,6 +639,35 @@ err: + } + + /* ++ * Returns 1 if the device is private LVM device. ++ */ ++int sysfs_devno_is_lvm_private(dev_t devno) ++{ ++ struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; ++ char *uuid = NULL; ++ int rc = 0; ++ ++ if (sysfs_init(&cxt, devno, NULL) != 0) ++ return 0; ++ ++ uuid = sysfs_strdup(&cxt, "dm/uuid"); ++ ++ /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important ++ * is the "LVM" prefix and "-<name>" postfix). ++ */ ++ if (uuid && strncmp(uuid, "LVM-", 4) == 0) { ++ char *p = strrchr(uuid + 4, '-'); ++ ++ if (p && *(p + 1)) ++ rc = 1; ++ } ++ ++ sysfs_deinit(&cxt); ++ free(uuid); ++ return rc; ++} ++ ++/* + * Return 0 or 1, or < 0 in case of error + */ + int sysfs_devno_is_wholedisk(dev_t devno) +@@ -651,6 +680,9 @@ int sysfs_devno_is_wholedisk(dev_t devno + return devno == disk; + } + ++ ++ ++ + int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, int *c, int *t, int *l) + { + char buf[PATH_MAX], *hctl; diff --git a/SOURCES/2.25-libblkid-xfs-log.patch b/SOURCES/2.25-libblkid-xfs-log.patch new file mode 100644 index 0000000..41f1ec8 --- /dev/null +++ b/SOURCES/2.25-libblkid-xfs-log.patch @@ -0,0 +1,133 @@ +diff -up util-linux-2.23.2/libblkid/src/superblocks/superblocks.c.kzak util-linux-2.23.2/libblkid/src/superblocks/superblocks.c +--- util-linux-2.23.2/libblkid/src/superblocks/superblocks.c.kzak 2015-06-24 12:58:20.790115492 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/superblocks.c 2015-06-24 12:57:57.961286166 +0200 +@@ -49,6 +49,8 @@ + * + * @UUID_SUB: subvolume uuid (e.g. btrfs) + * ++ * @LOGUUID: external log UUID (e.g. xfs) ++ * + * @UUID_RAW: raw UUID from FS superblock + * + * @EXT_JOURNAL: external journal UUID +@@ -113,6 +115,7 @@ static const struct blkid_idinfo *idinfo + &swsuspend_idinfo, + &swap_idinfo, + &xfs_idinfo, ++ &xfs_log_idinfo, + &ext4dev_idinfo, + &ext4_idinfo, + &ext3_idinfo, +diff -up util-linux-2.23.2/libblkid/src/superblocks/superblocks.h.kzak util-linux-2.23.2/libblkid/src/superblocks/superblocks.h +--- util-linux-2.23.2/libblkid/src/superblocks/superblocks.h.kzak 2015-06-24 12:58:48.533908071 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/superblocks.h 2015-06-24 12:59:00.098821476 +0200 +@@ -29,6 +29,7 @@ extern const struct blkid_idinfo ext2_id + extern const struct blkid_idinfo jbd_idinfo; + extern const struct blkid_idinfo jfs_idinfo; + extern const struct blkid_idinfo xfs_idinfo; ++extern const struct blkid_idinfo xfs_log_idinfo; + extern const struct blkid_idinfo gfs_idinfo; + extern const struct blkid_idinfo gfs2_idinfo; + extern const struct blkid_idinfo romfs_idinfo; +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 2015-06-24 12:39:34.300507071 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/xfs.c 2015-06-24 12:39:45.389427015 +0200 +@@ -4,6 +4,7 @@ + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> ++ * Copyright (C) 2013 Eric Sandeen <sandeen@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. +@@ -187,3 +188,90 @@ const struct blkid_idinfo xfs_idinfo = + } + }; + ++struct xlog_rec_header { ++ uint32_t h_magicno; ++ uint32_t h_dummy1[1]; ++ uint32_t h_version; ++ uint32_t h_len; ++ uint32_t h_dummy2[71]; ++ uint32_t h_fmt; ++ unsigned char h_uuid[16]; ++} __attribute__((packed)); ++ ++#define XLOG_HEADER_MAGIC_NUM 0xFEEDbabe ++ ++/* ++ * For very small filesystems, the minimum log size ++ * can be smaller, but that seems vanishingly unlikely ++ * when used with an external log (which is used for ++ * performance reasons; tiny conflicts with that goal). ++ */ ++#define XFS_MIN_LOG_BYTES (10 * 1024 * 1024) ++ ++#define XLOG_FMT_LINUX_LE 1 ++#define XLOG_FMT_LINUX_BE 2 ++#define XLOG_FMT_IRIX_BE 3 ++ ++#define XLOG_VERSION_1 1 ++#define XLOG_VERSION_2 2 /* Large IClogs, Log sunit */ ++#define XLOG_VERSION_OKBITS (XLOG_VERSION_1 | XLOG_VERSION_2) ++ ++static int xlog_valid_rec_header(struct xlog_rec_header *rhead) ++{ ++ uint32_t hlen; ++ ++ if (rhead->h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM)) ++ return 0; ++ ++ if (!rhead->h_version || ++ (be32_to_cpu(rhead->h_version) & (~XLOG_VERSION_OKBITS))) ++ return 0; ++ ++ /* LR body must have data or it wouldn't have been written */ ++ hlen = be32_to_cpu(rhead->h_len); ++ if (hlen <= 0 || hlen > INT_MAX) ++ return 0; ++ ++ if (rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_LE) && ++ rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_BE) && ++ rhead->h_fmt != cpu_to_be32(XLOG_FMT_IRIX_BE)) ++ return 0; ++ ++ return 1; ++} ++ ++/* xlog record header will be in some sector in the first 256k */ ++static int probe_xfs_log(blkid_probe pr, const struct blkid_idmag *mag) ++{ ++ int i; ++ struct xlog_rec_header *rhead; ++ unsigned char *buf; ++ ++ buf = blkid_probe_get_buffer(pr, 0, 256*1024); ++ if (!buf) ++ return errno ? -errno : 1; ++ ++ if (memcmp(buf, "XFSB", 4) == 0) ++ return 1; /* this is regular XFS, ignore */ ++ ++ /* check the first 512 512-byte sectors */ ++ for (i = 0; i < 512; i++) { ++ rhead = (struct xlog_rec_header *)&buf[i*512]; ++ ++ if (xlog_valid_rec_header(rhead)) { ++ blkid_probe_set_uuid_as(pr, rhead->h_uuid, "LOGUUID"); ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++const struct blkid_idinfo xfs_log_idinfo = ++{ ++ .name = "xfs_external_log", ++ .usage = BLKID_USAGE_OTHER, ++ .probefunc = probe_xfs_log, ++ .magics = BLKID_NONE_MAGIC, ++ .minsz = XFS_MIN_LOG_BYTES, ++}; 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-lscpu-discontinuous-NUMA-nodes.patch b/SOURCES/2.25-lscpu-discontinuous-NUMA-nodes.patch new file mode 100644 index 0000000..6428857 --- /dev/null +++ b/SOURCES/2.25-lscpu-discontinuous-NUMA-nodes.patch @@ -0,0 +1,102 @@ +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 2013-07-30 10:39:26.342739583 +0200 ++++ util-linux-2.23.2/sys-utils/lscpu.c 2014-01-14 11:21:51.837599200 +0100 +@@ -49,6 +49,7 @@ + /* /sys paths */ + #define _PATH_SYS_SYSTEM "/sys/devices/system" + #define _PATH_SYS_CPU _PATH_SYS_SYSTEM "/cpu" ++#define _PATH_SYS_NODE _PATH_SYS_SYSTEM "/node" + #define _PATH_PROC_XEN "/proc/xen" + #define _PATH_PROC_XENCAP _PATH_PROC_XEN "/capabilities" + #define _PATH_PROC_CPUINFO "/proc/cpuinfo" +@@ -157,6 +158,7 @@ struct lscpu_desc { + cpu_set_t *online; /* mask with online CPUs */ + + int nnodes; /* number of NUMA modes */ ++ int *idx2nodenum; /* Support for discontinuous nodes */ + cpu_set_t **nodemaps; /* array with NUMA nodes */ + + /* books -- based on book_siblings (internal kernel map of cpuX's +@@ -802,25 +804,59 @@ read_cache(struct lscpu_desc *desc, int + } + } + ++static inline int is_node_dirent(struct dirent *d) ++{ ++ return ++ d && ++#ifdef _DIRENT_HAVE_D_TYPE ++ d->d_type == DT_DIR && ++#endif ++ strncmp(d->d_name, "node", 4) == 0 && ++ isdigit_string(d->d_name + 4); ++} ++ + static void + read_nodes(struct lscpu_desc *desc) + { +- int i; ++ int i = 0; ++ DIR *dir; ++ struct dirent *d; ++ char *path; + + /* number of NUMA node */ +- while (path_exist(_PATH_SYS_SYSTEM "/node/node%d", desc->nnodes)) +- desc->nnodes++; ++ path = path_strdup(_PATH_SYS_NODE); ++ dir = opendir(path); ++ free(path); ++ ++ while (dir && (d = readdir(dir))) { ++ if (is_node_dirent(d)) ++ desc->nnodes++; ++ } + +- if (!desc->nnodes) ++ if (!desc->nnodes) { ++ if (dir) ++ closedir(dir); + return; ++ } + + desc->nodemaps = xcalloc(desc->nnodes, sizeof(cpu_set_t *)); ++ desc->idx2nodenum = xmalloc(desc->nnodes * sizeof(int)); ++ ++ if (dir) { ++ rewinddir(dir); ++ while ((d = readdir(dir)) && i < desc->nnodes) { ++ if (is_node_dirent(d)) ++ desc->idx2nodenum[i++] = strtol_or_err(((d->d_name) + 4), ++ _("Failed to extract the node number")); ++ } ++ closedir(dir); ++ } + + /* information about how nodes share different CPUs */ + for (i = 0; i < desc->nnodes; i++) + desc->nodemaps[i] = path_read_cpuset(maxcpus, + _PATH_SYS_SYSTEM "/node/node%d/cpumap", +- i); ++ desc->idx2nodenum[i]); + } + + static char * +@@ -850,7 +886,7 @@ get_cell_data(struct lscpu_desc *desc, i + case COL_NODE: + if (cpuset_ary_isset(cpu, desc->nodemaps, + desc->nnodes, setsize, &idx) == 0) +- snprintf(buf, bufsz, "%zd", idx); ++ snprintf(buf, bufsz, "%d", desc->idx2nodenum[idx]); + break; + case COL_BOOK: + if (cpuset_ary_isset(cpu, desc->bookmaps, +@@ -1250,7 +1286,7 @@ print_summary(struct lscpu_desc *desc, s + } + + for (i = 0; i < desc->nnodes; i++) { +- snprintf(buf, sizeof(buf), _("NUMA node%d CPU(s):"), i); ++ snprintf(buf, sizeof(buf), _("NUMA node%d CPU(s):"), desc->idx2nodenum[i]); + print_cpuset(buf, desc->nodemaps[i], mod->hex); + } + } diff --git a/SOURCES/2.25-lscpu-sort-NUMA.patch b/SOURCES/2.25-lscpu-sort-NUMA.patch new file mode 100644 index 0000000..2055d2d --- /dev/null +++ b/SOURCES/2.25-lscpu-sort-NUMA.patch @@ -0,0 +1,144 @@ +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-01-14 14:02:52.228996385 +0100 ++++ util-linux-2.23.2/sys-utils/lscpu.c 2014-01-14 14:04:08.109795733 +0100 +@@ -815,6 +815,13 @@ static inline int is_node_dirent(struct + isdigit_string(d->d_name + 4); + } + ++static int ++nodecmp(const void *ap, const void *bp) ++{ ++ int *a = (int *) ap, *b = (int *) bp; ++ return *a - *b; ++} ++ + static void + read_nodes(struct lscpu_desc *desc) + { +@@ -850,6 +857,7 @@ read_nodes(struct lscpu_desc *desc) + _("Failed to extract the node number")); + } + closedir(dir); ++ qsort(desc->idx2nodenum, desc->nnodes, sizeof(int), nodecmp); + } + + /* information about how nodes share different CPUs */ +diff -up util-linux-2.23.2/tests/expected/lscpu/lscpu-x86_64-64cpu.kzak util-linux-2.23.2/tests/expected/lscpu/lscpu-x86_64-64cpu +--- util-linux-2.23.2/tests/expected/lscpu/lscpu-x86_64-64cpu.kzak 2013-06-13 09:46:10.551651742 +0200 ++++ util-linux-2.23.2/tests/expected/lscpu/lscpu-x86_64-64cpu 2014-01-14 14:04:29.662022613 +0100 +@@ -4,7 +4,7 @@ On-line CPU(s) list: 0-63 + Thread(s) per core: 2 + Core(s) per socket: 8 + Socket(s): 4 +-NUMA node(s): 1 ++NUMA node(s): 3 + Vendor ID: GenuineIntel + CPU family: 6 + Model: 46 +@@ -18,72 +18,74 @@ L1i cache: 32K + L2 cache: 256K + L3 cache: 18432K + NUMA node0 CPU(s): 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62 ++NUMA node2 CPU(s): 1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61 ++NUMA node3 CPU(s): 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63 + + # The following is the parsable format, which can be fed to other + # programs. Each different item in every column has an unique ID + # starting from zero. + # CPU,Core,Socket,Node,,L1d,L1i,L2,L3 + 0,0,0,0,,0,0,0,0 +-1,1,1,,,1,1,1,1 ++1,1,1,2,,1,1,1,1 + 2,2,2,0,,2,2,2,2 +-3,3,3,,,3,3,3,3 ++3,3,3,3,,3,3,3,3 + 4,4,0,0,,4,4,4,0 +-5,5,1,,,5,5,5,1 ++5,5,1,2,,5,5,5,1 + 6,6,2,0,,6,6,6,2 +-7,7,3,,,7,7,7,3 ++7,7,3,3,,7,7,7,3 + 8,8,0,0,,8,8,8,0 +-9,9,1,,,9,9,9,1 ++9,9,1,2,,9,9,9,1 + 10,10,2,0,,10,10,10,2 +-11,11,3,,,11,11,11,3 ++11,11,3,3,,11,11,11,3 + 12,12,0,0,,12,12,12,0 +-13,13,1,,,13,13,13,1 ++13,13,1,2,,13,13,13,1 + 14,14,2,0,,14,14,14,2 +-15,15,3,,,15,15,15,3 ++15,15,3,3,,15,15,15,3 + 16,16,0,0,,16,16,16,0 +-17,17,1,,,17,17,17,1 ++17,17,1,2,,17,17,17,1 + 18,18,2,0,,18,18,18,2 +-19,19,3,,,19,19,19,3 ++19,19,3,3,,19,19,19,3 + 20,20,0,0,,20,20,20,0 +-21,21,1,,,21,21,21,1 ++21,21,1,2,,21,21,21,1 + 22,22,2,0,,22,22,22,2 +-23,23,3,,,23,23,23,3 ++23,23,3,3,,23,23,23,3 + 24,24,0,0,,24,24,24,0 +-25,25,1,,,25,25,25,1 ++25,25,1,2,,25,25,25,1 + 26,26,2,0,,26,26,26,2 +-27,27,3,,,27,27,27,3 ++27,27,3,3,,27,27,27,3 + 28,28,0,0,,28,28,28,0 +-29,29,1,,,29,29,29,1 ++29,29,1,2,,29,29,29,1 + 30,30,2,0,,30,30,30,2 +-31,31,3,,,31,31,31,3 ++31,31,3,3,,31,31,31,3 + 32,0,0,0,,0,0,0,0 +-33,1,1,,,1,1,1,1 ++33,1,1,2,,1,1,1,1 + 34,2,2,0,,2,2,2,2 +-35,3,3,,,3,3,3,3 ++35,3,3,3,,3,3,3,3 + 36,4,0,0,,4,4,4,0 +-37,5,1,,,5,5,5,1 ++37,5,1,2,,5,5,5,1 + 38,6,2,0,,6,6,6,2 +-39,7,3,,,7,7,7,3 ++39,7,3,3,,7,7,7,3 + 40,8,0,0,,8,8,8,0 +-41,9,1,,,9,9,9,1 ++41,9,1,2,,9,9,9,1 + 42,10,2,0,,10,10,10,2 +-43,11,3,,,11,11,11,3 ++43,11,3,3,,11,11,11,3 + 44,12,0,0,,12,12,12,0 +-45,13,1,,,13,13,13,1 ++45,13,1,2,,13,13,13,1 + 46,14,2,0,,14,14,14,2 +-47,15,3,,,15,15,15,3 ++47,15,3,3,,15,15,15,3 + 48,16,0,0,,16,16,16,0 +-49,17,1,,,17,17,17,1 ++49,17,1,2,,17,17,17,1 + 50,18,2,0,,18,18,18,2 +-51,19,3,,,19,19,19,3 ++51,19,3,3,,19,19,19,3 + 52,20,0,0,,20,20,20,0 +-53,21,1,,,21,21,21,1 ++53,21,1,2,,21,21,21,1 + 54,22,2,0,,22,22,22,2 +-55,23,3,,,23,23,23,3 ++55,23,3,3,,23,23,23,3 + 56,24,0,0,,24,24,24,0 +-57,25,1,,,25,25,25,1 ++57,25,1,2,,25,25,25,1 + 58,26,2,0,,26,26,26,2 +-59,27,3,,,27,27,27,3 ++59,27,3,3,,27,27,27,3 + 60,28,0,0,,28,28,28,0 +-61,29,1,,,29,29,29,1 ++61,29,1,2,,29,29,29,1 + 62,30,2,0,,30,30,30,2 +-63,31,3,,,31,31,31,3 ++63,31,3,3,,31,31,31,3 diff --git a/SOURCES/2.25-mount-man-default.patch b/SOURCES/2.25-mount-man-default.patch new file mode 100644 index 0000000..f137025 --- /dev/null +++ b/SOURCES/2.25-mount-man-default.patch @@ -0,0 +1,27 @@ +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 2015-06-24 10:44:54.888929869 +0200 ++++ util-linux-2.23.2/sys-utils/mount.8 2015-06-24 10:48:10.022436703 +0200 +@@ -865,9 +865,10 @@ Some of these options are only useful wh + .I /etc/fstab + file. + +-Some of these options could be enabled or disabled by default +-in the system kernel. To check the current setting see the options +-in /proc/mounts. ++Some of these options could be enabled or disabled by default in the system ++kernel. To check the current setting see the options in /proc/mounts. Note ++that filesystems also have per-filesystem specific default mount options (see ++for example tune2fs -l output for extN filesystems). + + The following options apply to any filesystem that is being + mounted (but not every filesystem actually honors them - e.g., the +@@ -973,6 +974,9 @@ For more details, see + .B defaults + Use default options: + .BR rw ", " suid ", " dev ", " exec ", " auto ", " nouser ", and " async. ++ ++Note that the real set of the all default mount options depends on kernel ++and filesystem type. See the begin of this section for more details. + .TP + .B dev + Interpret character or block special devices on the filesystem. diff --git a/SOURCES/2.25-mount-man-xfs.patch b/SOURCES/2.25-mount-man-xfs.patch new file mode 100644 index 0000000..b8973cd --- /dev/null +++ b/SOURCES/2.25-mount-man-xfs.patch @@ -0,0 +1,16 @@ +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-03-12 12:35:46.532369960 +0100 ++++ util-linux-2.23.2/sys-utils/mount.8 2014-03-12 12:35:23.041126143 +0100 +@@ -2598,9 +2598,9 @@ None. + .TP + .BR allocsize=size + Sets the buffered I/O end-of-file preallocation size when +-doing delayed allocation writeout (default size is 64KiB). +-Valid values for this option are page size (typically 4KiB) +-through to 1GiB, inclusive, in power-of-2 increments. ++doing delayed allocation writeout. Valid values for this ++option are page size (typically 4KiB) through to 1GiB, ++inclusive, in power-of-2 increments. + .sp + The default behaviour is for dynamic end-of-file + preallocation size, which uses a set of heuristics to 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] [<spec>]\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 <prio> specify the priority of the swap device\n" +- " -s, --summary display summary about used swap devices\n" +- " --show[=<columns>] 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[=<policy>] 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 <prio> specify the priority of the swap device\n" ++ " -s, --summary display summary about used swap devices\n" ++ " --show[=<columns>] 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__ + " <device> name of device to be used\n" + " <file> 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-taskset-man-fix-permissions.patch b/SOURCES/2.25-taskset-man-fix-permissions.patch new file mode 100644 index 0000000..ef97f50 --- /dev/null +++ b/SOURCES/2.25-taskset-man-fix-permissions.patch @@ -0,0 +1,36 @@ +From ab0e0fa7a45bccf8304edcb2a904f30a4f3a48b1 Mon Sep 17 00:00:00 2001 +From: Rik van Riel <riel@redhat.com> +Date: Fri, 6 Dec 2013 16:07:54 -0500 +Subject: [PATCH] taskset: fix PERMISSIONS section of taskset man page + +A user is always allowed to change the CPU affinity of his or her +own processes. CAP_SYS_NICE is only required to change the affinity +of another user's process. + +Signed-off-by: Rik van Riel <riel@redhat.com> +Reported-by: Joe Mario <jmario@redhat.com> +--- + schedutils/taskset.1 | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/schedutils/taskset.1 b/schedutils/taskset.1 +index ade202b..fb5738c 100644 +--- a/schedutils/taskset.1 ++++ b/schedutils/taskset.1 +@@ -102,10 +102,11 @@ Or set it: + .B taskset \-p + .I mask pid + .SH PERMISSIONS ++A user can change the CPU affinity of a process belonging to the same user. + A user must possess + .B CAP_SYS_NICE +-to change the CPU affinity of a process. Any user can retrieve the affinity +-mask. ++to change the CPU affinity of a process belonging to another user. ++A user can retrieve the affinity mask of any process. + .SH AUTHOR + Written by Robert M. Love. + .SH COPYRIGHT +-- +1.8.4.2 + diff --git a/SOURCES/2.25-uuidd-timeout.patch b/SOURCES/2.25-uuidd-timeout.patch new file mode 100644 index 0000000..850c688 --- /dev/null +++ b/SOURCES/2.25-uuidd-timeout.patch @@ -0,0 +1,12 @@ +diff -up util-linux-2.23.2/misc-utils/uuidd.service.in.kzak util-linux-2.23.2/misc-utils/uuidd.service.in +--- util-linux-2.23.2/misc-utils/uuidd.service.in.kzak 2013-02-27 17:46:29.903020913 +0100 ++++ util-linux-2.23.2/misc-utils/uuidd.service.in 2015-04-20 11:31:44.614013049 +0200 +@@ -3,7 +3,7 @@ Description=Daemon for generating UUIDs + Requires=uuidd.socket + + [Service] +-ExecStart=@usrsbin_execdir@/uuidd --socket-activation --timeout 60 ++ExecStart=@usrsbin_execdir@/uuidd --socket-activation + Restart=no + User=uuidd + Group=uuidd diff --git a/SOURCES/2.25-wipefs-call-BLKRRPART-when-erase-partition-table.patch b/SOURCES/2.25-wipefs-call-BLKRRPART-when-erase-partition-table.patch new file mode 100644 index 0000000..260b1ad --- /dev/null +++ b/SOURCES/2.25-wipefs-call-BLKRRPART-when-erase-partition-table.patch @@ -0,0 +1,140 @@ +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 2013-07-30 10:39:26.232738496 +0200 ++++ util-linux-2.23.2/misc-utils/wipefs.8 2014-01-23 11:07:54.390022299 +0100 +@@ -23,6 +23,9 @@ does not erase the filesystem itself nor + When used without options \fB-a\fR or \fB-o\fR, it lists all visible filesystems + and the offsets of their basic signatures. + ++.B wipefs ++calls BLKRRPART ioctl when erase partition table to inform kernel about the change. ++ + Note that some filesystems or some partition tables store more magic strings on + the devices. The + .B wipefs +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 2013-07-30 10:39:26.232738496 +0200 ++++ util-linux-2.23.2/misc-utils/wipefs.c 2014-01-23 11:12:26.786860550 +0100 +@@ -40,21 +40,24 @@ + #include "c.h" + #include "closestream.h" + #include "optutils.h" ++#include "blkdev.h" + + struct wipe_desc { + loff_t offset; /* magic string offset */ + size_t len; /* length of magic string */ + unsigned char *magic; /* magic string */ + +- int zap; /* zap this offset? */ + char *usage; /* raid, filesystem, ... */ + char *type; /* FS type */ + char *label; /* FS label */ + char *uuid; /* FS uuid */ + +- int on_disk; +- + struct wipe_desc *next; ++ ++ unsigned int zap : 1, ++ on_disk : 1, ++ is_parttable : 1; ++ + }; + + enum { +@@ -72,7 +75,7 @@ print_pretty(struct wipe_desc *wp, int l + printf("----------------------------------------------------------------\n"); + } + +- printf("0x%-17jx %s [%s]", wp->offset, wp->type, wp->usage); ++ printf("0x%-17jx %s [%s]", wp->offset, wp->type, _(wp->usage)); + + if (wp->label && *wp->label) + printf("\n%27s %s", "LABEL:", wp->label); +@@ -141,7 +144,7 @@ add_offset(struct wipe_desc *wp0, loff_t + wp = xcalloc(1, sizeof(struct wipe_desc)); + wp->offset = offset; + wp->next = wp0; +- wp->zap = zap; ++ wp->zap = zap ? 1 : 0; + return wp; + } + +@@ -164,7 +167,7 @@ get_desc_for_probe(struct wipe_desc *wp, + const char *off, *type, *mag, *p, *usage = NULL; + size_t len; + loff_t offset; +- int rc; ++ int rc, ispt = 0; + + /* superblocks */ + if (blkid_probe_lookup_value(pr, "TYPE", &type, NULL) == 0) { +@@ -181,7 +184,8 @@ get_desc_for_probe(struct wipe_desc *wp, + rc = blkid_probe_lookup_value(pr, "PTMAGIC", &mag, &len); + if (rc) + return wp; +- usage = "partition table"; ++ usage = N_("partition table"); ++ ispt = 1; + } else + return wp; + +@@ -199,6 +203,7 @@ get_desc_for_probe(struct wipe_desc *wp, + + wp->type = xstrdup(type); + wp->on_disk = 1; ++ wp->is_parttable = ispt ? 1 : 0; + + wp->magic = xmalloc(len); + memcpy(wp->magic, mag, len); +@@ -309,10 +314,25 @@ static void do_wipe_real(blkid_probe pr, + putchar('\n'); + } + ++ ++static void rereadpt(int fd, const char *devname) ++{ ++#ifdef BLKRRPART ++ struct stat st; ++ ++ if (fstat(fd, &st) || !S_ISBLK(st.st_mode)) ++ return; ++ ++ errno = 0; ++ ioctl(fd, BLKRRPART); ++ printf(_("%s: calling ioclt to re-read partition table: %m\n"), devname); ++#endif ++} ++ + static struct wipe_desc * + do_wipe(struct wipe_desc *wp, const char *devname, int noact, int all, int quiet, int force) + { +- int flags; ++ int flags, reread = 0; + blkid_probe pr; + struct wipe_desc *w, *wp0; + int zap = all ? 1 : wp->zap; +@@ -345,8 +365,11 @@ do_wipe(struct wipe_desc *wp, const char + if (!wp->on_disk) + continue; + +- if (zap) ++ if (zap) { + do_wipe_real(pr, devname, wp, noact, quiet); ++ if (wp->is_parttable) ++ reread = 1; ++ } + } + + for (w = wp0; w != NULL; w = w->next) { +@@ -355,6 +378,10 @@ do_wipe(struct wipe_desc *wp, const char + } + + fsync(blkid_probe_get_fd(pr)); ++ ++ if (reread) ++ rereadpt(blkid_probe_get_fd(pr), devname); ++ + close(blkid_probe_get_fd(pr)); + blkid_free_probe(pr); + free_wipe(wp0); 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 <fcntl.h> + #include <limits.h> + #include <getopt.h> ++#include <time.h> + + #include <sys/ioctl.h> + #include <sys/stat.h> ++#include <sys/time.h> + #include <linux/fs.h> + + #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 <num> offset in bytes to discard from\n" + " -l, --length <num> length of bytes to discard from the offset\n" ++ " -p, --step <num> 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-libblkid-fat.patch b/SOURCES/2.26-libblkid-fat.patch new file mode 100644 index 0000000..f6f87ad --- /dev/null +++ b/SOURCES/2.26-libblkid-fat.patch @@ -0,0 +1,113 @@ +diff -up util-linux-2.23.2/libblkid/src/partitions/dos.c.kzak util-linux-2.23.2/libblkid/src/partitions/dos.c +--- util-linux-2.23.2/libblkid/src/partitions/dos.c.kzak 2015-08-21 10:16:45.244332027 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/dos.c 2015-08-21 10:22:07.181340367 +0200 +@@ -151,16 +151,6 @@ static int probe_dos_pt(blkid_probe pr, + if (memcmp(data, BLKID_AIX_MAGIC_STRING, BLKID_AIX_MAGIC_STRLEN) == 0) + goto nothing; + +- /* +- * Now that the 55aa signature is present, this is probably +- * either the boot sector of a FAT filesystem or a DOS-type +- * partition table. +- */ +- if (blkid_probe_is_vfat(pr) == 1) { +- DBG(LOWPROBE, blkid_debug("probably FAT -- ignore")); +- goto nothing; +- } +- + p0 = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET); + + /* +@@ -182,6 +172,16 @@ static int probe_dos_pt(blkid_probe pr, + } + } + ++ /* ++ * Now that the 55aa signature is present, this is probably ++ * either the boot sector of a FAT filesystem or a DOS-type ++ * partition table. ++ */ ++ if (blkid_probe_is_vfat(pr) == 1) { ++ DBG(LOWPROBE, blkid_debug("probably FAT -- ignore")); ++ goto nothing; ++ } ++ + blkid_probe_use_wiper(pr, BLKID_MSDOS_PT_OFFSET, + 512 - BLKID_MSDOS_PT_OFFSET); + +diff -up util-linux-2.23.2/libblkid/src/partitions/dos.h.kzak util-linux-2.23.2/libblkid/src/partitions/dos.h +--- util-linux-2.23.2/libblkid/src/partitions/dos.h.kzak 2015-08-21 10:34:14.919380422 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/dos.h 2015-08-21 10:35:45.221807669 +0200 +@@ -12,6 +12,12 @@ struct dos_partition { + + #define BLKID_MSDOS_PT_OFFSET 0x1be + ++static inline struct dos_partition *mbr_get_partition(unsigned char *mbr, int i) ++{ ++ return (struct dos_partition *) ++ (mbr + BLKID_MSDOS_PT_OFFSET + (i * sizeof(struct dos_partition))); ++} ++ + /* assemble badly aligned little endian integer */ + static inline unsigned int assemble4le(const unsigned char *p) + { +diff -up util-linux-2.23.2/libblkid/src/superblocks/vfat.c.kzak util-linux-2.23.2/libblkid/src/superblocks/vfat.c +--- util-linux-2.23.2/libblkid/src/superblocks/vfat.c.kzak 2015-08-21 10:10:12.111906711 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/vfat.c 2015-08-21 10:35:07.733045452 +0200 +@@ -16,6 +16,7 @@ + #include <ctype.h> + #include <stdint.h> + ++#include "partitions/dos.h" + #include "superblocks.h" + + /* Yucky misaligned values */ +@@ -169,7 +170,8 @@ static unsigned char *search_fat_label(b + return NULL; + } + +-static int fat_valid_superblock(const struct blkid_idmag *mag, ++static int fat_valid_superblock(blkid_probe pr, ++ const struct blkid_idmag *mag, + struct msdos_super_block *ms, + struct vfat_super_block *vs, + uint32_t *cluster_count, uint32_t *fat_size) +@@ -243,6 +245,20 @@ static int fat_valid_superblock(const st + if (cluster_count) + *cluster_count = __cluster_count; + ++ if (blkid_probe_is_wholedisk(pr)) { ++ /* OK, seems like FAT, but it's possible that we found boot ++ * sector with crazy FAT-like stuff (magic strings, media, ++ * etc..) before MBR. Let's make sure that there is no MBR with ++ * usable partition. */ ++ unsigned char *buf = (unsigned char *) ms; ++ if (is_valid_mbr_signature(buf)) { ++ struct dos_partition *p0 = mbr_get_partition(buf, 0); ++ if (dos_partition_size(p0) != 0 && ++ (p0->boot_ind == 0 || p0->boot_ind == 0x80)) ++ return 0; ++ } ++ } ++ + return 1; /* valid */ + } + +@@ -270,7 +286,7 @@ int blkid_probe_is_vfat(blkid_probe pr) + if (!vs) + return errno ? -errno : 0; + +- return fat_valid_superblock(mag, ms, vs, NULL, NULL); ++ return fat_valid_superblock(pr, mag, ms, vs, NULL, NULL); + } + + /* FAT label extraction from the root directory taken from Kay +@@ -293,7 +309,7 @@ static int probe_vfat(blkid_probe pr, co + if (!vs) + return errno ? -errno : 1; + +- if (!fat_valid_superblock(mag, ms, vs, &cluster_count, &fat_size)) ++ if (!fat_valid_superblock(pr, mag, ms, vs, &cluster_count, &fat_size)) + return 1; + + sector_size = unaligned_le16(&ms->ms_sector_size); 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 <ooprala@redhat.com> ++ * ++ * 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 <stdarg.h> ++#include <string.h> ++ ++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 <kzak@redhat.com> + + 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 <http://www.gnu.org/licenses/>. */ +- +-#include <stddef.h> ++#ifndef UTIL_LINUX_MBSALIGN_H ++# define UTIL_LINUX_MBSALIGN_H ++# include <stddef.h> + + 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 <stdio.h> + #include <stdbool.h> + #include <limits.h> ++#include <ctype.h> + + #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 @@ ++<?xml version="1.0"?> ++<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" ++ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" ++[ ++ <!ENTITY version SYSTEM "version.xml"> ++]> ++<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude"> ++ <bookinfo> ++ <title>libsmartcols Reference Manual</title> ++ <releaseinfo>for libsmartcols version &version;</releaseinfo> ++ <copyright> ++ <year>2014</year> ++ <holder>Karel Zak <kzak@redhat.com></holder> ++ </copyright> ++ </bookinfo> ++ ++ <part id="gtk"> ++ <title>libsmartcols Overview</title> ++ <partintro> ++ <para> ++The libsmartcols library is used for smart adaptive formatting of tabular data. ++ </para> ++ <para> ++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/. ++ </para> ++ </partintro> ++ </part> ++ ++ <part> ++ <title>Data manipulation</title> ++ <xi:include href="xml/table.xml"/> ++ <xi:include href="xml/column.xml"/> ++ <xi:include href="xml/line.xml"/> ++ <xi:include href="xml/cell.xml"/> ++ <xi:include href="xml/symbols.xml"/> ++ </part> ++ <part> ++ <title>Printing</title> ++ <xi:include href="xml/table_print.xml"/> ++ </part> ++ <part> ++ <title>Misc</title> ++ <xi:include href="xml/iter.xml"/> ++ <xi:include href="xml/version-utils.xml"/> ++ <xi:include href="xml/init.xml"/> ++ </part> ++ <index id="api-index-full"> ++ <title>API Index</title> ++ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include> ++ </index> ++</book> +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 @@ ++<SECTION> ++<FILE>cell</FILE> ++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 ++</SECTION> ++ ++<SECTION> ++<FILE>column</FILE> ++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 ++</SECTION> ++ ++<SECTION> ++<FILE>iter</FILE> ++libscols_iter ++scols_free_iter ++scols_iter_get_direction ++scols_new_iter ++scols_reset_iter ++</SECTION> ++ ++<SECTION> ++<FILE>line</FILE> ++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 ++</SECTION> ++ ++<SECTION> ++<FILE>symbols</FILE> ++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 ++</SECTION> ++ ++<SECTION> ++<FILE>table</FILE> ++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 ++</SECTION> ++ ++<SECTION> ++<FILE>table_print</FILE> ++scols_print_table ++scols_print_table_to_string ++</SECTION> ++ ++<SECTION> ++<FILE>version-utils</FILE> ++scols_get_library_version ++scols_parse_version_string ++LIBSMARTCOLS_VERSION ++</SECTION> ++ ++<SECTION> ++<FILE>init</FILE> ++scols_init_debug ++</SECTION> +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 <ooprala@redhat.com> ++ * Copyright (C) 2014 Karel Zak <kzak@redhat.com> ++ * ++ * 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 <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <ctype.h> ++ ++#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 <ooprala@redhat.com> ++ * Copyright (C) 2014 Karel Zak <kzak@redhat.com> ++ * ++ * 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 <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <ctype.h> ++ ++#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 <kzak@redhat.com> ++ * ++ * 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 <stdarg.h> ++ ++#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 <kzak@redhat.com> ++ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com> ++ * ++ * 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 <string.h> ++#include <stdlib.h> ++ ++#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 <ooprala@redhat.com> ++ * Copyright (C) 2014 Karel Zak <kzak@redhat.com> ++ * ++ * 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 <stdlib.h> ++#include <stdio.h> ++#include <sys/types.h> ++ ++/** ++ * 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 <kzak@redhat.com> ++ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com> ++ * ++ * 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 <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <ctype.h> ++ ++#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 <ooprala@redhat.com> ++ * Copyright (C) 2014 Karel Zak <kzak@redhat.com> ++ * ++ * 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 <assert.h> ++#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 <ooprala@redhat.com> ++ * ++ * 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 <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++ ++#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 <kzak@redhat.com> ++ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com> ++ * ++ * 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 <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <termios.h> ++#include <ctype.h> ++ ++#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 <kzak@redhat.com> ++ * ++ * 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 <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <termios.h> ++#include <ctype.h> ++ ++#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 <kzak@redhat.com> ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++#include <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <errno.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <dirent.h> ++#include <getopt.h> ++ ++#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] [<dir> ...]\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 <kzak@redhat.com> ++ * ++ * 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 <ctype.h> ++ ++#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-logger-man-kern.patch b/SOURCES/2.26-logger-man-kern.patch new file mode 100644 index 0000000..b7b9434 --- /dev/null +++ b/SOURCES/2.26-logger-man-kern.patch @@ -0,0 +1,72 @@ +diff -up util-linux-2.23.2/misc-utils/logger.1.kzak util-linux-2.23.2/misc-utils/logger.1 +--- util-linux-2.23.2/misc-utils/logger.1.kzak 2015-06-24 12:18:23.938735038 +0200 ++++ util-linux-2.23.2/misc-utils/logger.1 2015-06-24 12:18:33.154667053 +0200 +@@ -125,28 +125,49 @@ flag is not provided, standard input is + The + .B logger + utility exits 0 on success, and >0 if an error occurs. +-.PP ++.SH FACILITIES AND LEVELS + Valid facility names are: +-.IR auth , \ authpriv +-(for security information of a sensitive nature), +-.IR cron , \ daemon , \ ftp , \ kern +-(can't be generated from user process), +-.IR lpr , \ mail , \ news , \ security +-(deprecated synonym for +-.IR auth ), \ syslog , \ user , \ uucp , +-and +-.IR local0 \ to \ local7 , +-inclusive. ++.IP ++.TS ++tab(:); ++left l l. ++\fBauth ++\fBauthpriv\fR:for security information of a sensitive nature ++\fBcron ++\fBdaemon ++\fBftp ++\fBkern\fR:cannot be generated from userspace process, automatically converted to \fBuser ++\fBlpr ++\fBmail ++\fBnews ++\fBsyslog ++\fBuser ++\fBuucp ++\fBlocal0 ++ to: ++\fBlocal7 ++\fBsecurity\fR:deprecated synonym for \fBauth ++.TE + .PP + Valid level names are: +-.IR alert , \ crit , \ debug , \ emerg , \ err , \ error +-(deprecated synonym for +-.IR err ), \ info , \ notice , \ panic +-(deprecated synonym for +-.IR emerg ), \ warning , \ warn +-(deprecated synonym for +-.IR warning ). +-For the priority order and intended purposes of these levels, see ++.IP ++.TS ++tab(:); ++left l l. ++\fBemerg ++\fBalert ++\fBcrit ++\fBerr ++\fBwarning ++\fBnotice ++\fBinfo ++\fBdebug ++\fBpanic\fR:deprecated synonym for \fBemerg ++\fBerror\fR:deprecated synonym for \fBerr ++\fBwarn\fR:deprecated synonym for \fBwarning ++.TE ++.PP ++For the priority order and intended purposes of these facilities and levels, see + .BR syslog (3). + .SH EXAMPLES + logger System rebooted diff --git a/SOURCES/2.26-login-SIGXFSZ.patch b/SOURCES/2.26-login-SIGXFSZ.patch new file mode 100644 index 0000000..0f49c9a --- /dev/null +++ b/SOURCES/2.26-login-SIGXFSZ.patch @@ -0,0 +1,39 @@ +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 2015-06-24 11:03:45.123285155 +0200 ++++ util-linux-2.23.2/login-utils/login.c 2015-06-24 11:46:19.168114438 +0200 +@@ -495,6 +495,7 @@ static void log_audit(struct login_conte + + static void log_lastlog(struct login_context *cxt) + { ++ struct sigaction sa, oldsa_xfsz; + struct lastlog ll; + time_t t; + int fd; +@@ -502,9 +503,14 @@ static void log_lastlog(struct login_con + if (!cxt->pwd) + return; + ++ /* lastlog is huge on systems with large UIDs, ignore SIGXFSZ */ ++ memset(&sa, 0, sizeof(sa)); ++ sa.sa_handler = SIG_IGN; ++ sigaction(SIGXFSZ, &sa, &oldsa_xfsz); ++ + fd = open(_PATH_LASTLOG, O_RDWR | O_CREAT, 0); + if (fd < 0) +- return; ++ goto done; + + if (lseek(fd, (off_t) cxt->pwd->pw_uid * sizeof(ll), SEEK_SET) == -1) + goto done; +@@ -542,7 +548,10 @@ static void log_lastlog(struct login_con + if (write_all(fd, (char *)&ll, sizeof(ll))) + warn(_("write lastlog failed")); + done: +- close(fd); ++ if (fd >= 0) ++ close(fd); ++ ++ sigaction(SIGXFSZ, &oldsa_xfsz, NULL); /* restore original setting */ + } + + /* diff --git a/SOURCES/2.26-lsblk-mpath.patch b/SOURCES/2.26-lsblk-mpath.patch new file mode 100644 index 0000000..1e7fae2 --- /dev/null +++ b/SOURCES/2.26-lsblk-mpath.patch @@ -0,0 +1,64 @@ +diff -up util-linux-2.23.2/misc-utils/lsblk.c.kzak util-linux-2.23.2/misc-utils/lsblk.c +--- util-linux-2.23.2/misc-utils/lsblk.c.kzak 2015-06-25 11:01:10.543344225 +0200 ++++ util-linux-2.23.2/misc-utils/lsblk.c 2015-06-25 11:14:17.085404974 +0200 +@@ -953,11 +953,13 @@ static void set_tt_data(struct blkdev_cx + }; + } + +-static void print_device(struct blkdev_cxt *cxt, struct tt_line *tt_parent) ++static void fill_table_line(struct blkdev_cxt *cxt, struct tt_line *tt_parent) + { + int i; + + cxt->tt_line = tt_add_line(lsblk->tt, tt_parent); ++ if (!cxt->tt_line) ++ return; + + for (i = 0; i < ncolumns; i++) + set_tt_data(cxt, i, get_column_id(i), cxt->tt_line); +@@ -1084,7 +1086,7 @@ static int list_partitions(struct blkdev + goto next; + + wholedisk_cxt->parent = &part_cxt; +- print_device(&part_cxt, parent_cxt ? parent_cxt->tt_line : NULL); ++ fill_table_line(&part_cxt, parent_cxt ? parent_cxt->tt_line : NULL); + if (!lsblk->nodeps) + process_blkdev(wholedisk_cxt, &part_cxt, 0, NULL); + } else { +@@ -1098,7 +1100,7 @@ static int list_partitions(struct blkdev + + /* Print whole disk only once */ + if (r) +- print_device(wholedisk_cxt, parent_cxt ? parent_cxt->tt_line : NULL); ++ fill_table_line(wholedisk_cxt, parent_cxt ? parent_cxt->tt_line : NULL); + if (ps == 0 && !lsblk->nodeps) + process_blkdev(&part_cxt, wholedisk_cxt, 0, NULL); + } +@@ -1171,9 +1173,11 @@ static int list_deps(struct blkdev_cxt * + process_blkdev(&dep, cxt, 1, d->d_name); + } + /* The dependency is a whole device. */ +- else if (!set_cxt(&dep, cxt, NULL, d->d_name)) +- process_blkdev(&dep, cxt, 1, NULL); +- ++ else if (!set_cxt(&dep, cxt, NULL, d->d_name)) { ++ /* For inverse tree we don't want to show partitions ++ * if the dependence is pn whle-disk */ ++ process_blkdev(&dep, cxt, lsblk->inverse ? 0 : 1, NULL); ++ } + reset_blkdev_cxt(&dep); + } + closedir(dir); +@@ -1185,9 +1189,10 @@ static int process_blkdev(struct blkdev_ + int do_partitions, const char *part_name) + { + if (do_partitions && cxt->npartitions) +- return list_partitions(cxt, parent, part_name); ++ list_partitions(cxt, parent, part_name); /* partitoins + whole-disk */ ++ else ++ fill_table_line(cxt, parent ? parent->tt_line : NULL); /* whole-disk only */ + +- print_device(cxt, parent ? parent->tt_line : NULL); + return list_deps(cxt); + } + 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 <http://www.gnu.org/licenses/>. */ ++ ++/* Written by jla; revised by djm */ ++ ++#ifndef READUTMP_H ++#define READUTMP_H ++ ++#include <sys/types.h> ++#include <utmp.h> ++ ++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 <http://www.gnu.org/licenses/>. */ ++ ++/* Written by jla; revised by djm */ ++/* extracted for util-linux by ooprala */ ++ ++#include <errno.h> ++#include <stdio.h> ++ ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <signal.h> ++#include <stdbool.h> ++#include <string.h> ++#include <stdlib.h> ++#include <stdint.h> ++ ++#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 <kzak@redhat.com> (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 <stdlib.h> + #include <string.h> + #include <sys/syslog.h> ++#include <sys/stat.h> ++#include <sys/types.h> ++#include <pwd.h> + + #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 <kzak@redhat.com> (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 <ooprala@redhat.com> ++ * Copyright (C) 2014 Karel Zak <kzak@redhat.com> ++ * ++ * 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 <stdio.h> ++#include <stdlib.h> ++#include <unistd.h> ++#include <getopt.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <sys/syslog.h> ++#include <pwd.h> ++#include <grp.h> ++#include <shadow.h> ++#include <paths.h> ++#include <time.h> ++#include <utmp.h> ++#include <signal.h> ++#include <err.h> ++#include <limits.h> ++#include <ctype.h> ++ ++#include <search.h> ++ ++#include <libsmartcols.h> ++#ifdef HAVE_LIBSELINUX ++# include <selinux/selinux.h> ++#endif ++ ++#ifdef HAVE_LIBSYSTEMD ++# include <systemd/sd-journal.h> ++#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=<groups> display users belonging to a group in <groups>\n"), out); ++ fputs(_(" -L, --last show info about the users' last login sessions\n"), out); ++ fputs(_(" -l, --logins=<logins> display only users from <logins>\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[=<list>] 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=<type> 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 <path> set an alternate path for wtmp\n"), out); ++ fputs(_(" --btmp-file <path> 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 <username> */ ++ 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/SOURCES/2.26-su-coredump-message.patch b/SOURCES/2.26-su-coredump-message.patch new file mode 100644 index 0000000..8d2b9ba --- /dev/null +++ b/SOURCES/2.26-su-coredump-message.patch @@ -0,0 +1,17 @@ +diff -up util-linux-2.23.2/login-utils/su-common.c.kzak util-linux-2.23.2/login-utils/su-common.c +--- util-linux-2.23.2/login-utils/su-common.c.kzak 2015-06-24 11:14:25.102395082 +0200 ++++ util-linux-2.23.2/login-utils/su-common.c 2015-06-24 11:22:20.472859684 +0200 +@@ -359,10 +359,9 @@ create_watching_parent (void) + if (pid != (pid_t)-1) + if (WIFSIGNALED (status)) + { +- status = WTERMSIG (status) + 128; +- if (WCOREDUMP (status)) +- fprintf (stderr, _("%s (core dumped)\n"), +- strsignal (WTERMSIG (status))); ++ fprintf (stderr, "%s%s\n", strsignal (WTERMSIG (status)), ++ WCOREDUMP (status) ? _(" (core dumped)") : ""); ++ status = WTERMSIG (status) + 128; + } + else + status = WEXITSTATUS (status); diff --git a/SOURCES/2.26-unshare-rebase.patch b/SOURCES/2.26-unshare-rebase.patch new file mode 100644 index 0000000..672ff36 --- /dev/null +++ b/SOURCES/2.26-unshare-rebase.patch @@ -0,0 +1,757 @@ +diff -up util-linux-2.23.2/include/pathnames.h.kzak util-linux-2.23.2/include/pathnames.h +--- util-linux-2.23.2/include/pathnames.h.kzak 2015-06-26 10:00:19.111877564 +0200 ++++ util-linux-2.23.2/include/pathnames.h 2015-06-26 10:00:51.623630869 +0200 +@@ -85,6 +85,10 @@ + #define _PATH_PROC_LOCKS "/proc/locks" + #define _PATH_PROC_CDROMINFO "/proc/sys/dev/cdrom/info" + ++#define _PATH_PROC_UIDMAP "/proc/self/uid_map" ++#define _PATH_PROC_GIDMAP "/proc/self/gid_map" ++#define _PATH_PROC_SETGROUPS "/proc/self/setgroups" ++ + #define _PATH_PROC_ATTR_CURRENT "/proc/self/attr/current" + #define _PATH_PROC_ATTR_EXEC "/proc/self/attr/exec" + #define _PATH_PROC_CAPLASTCAP "/proc/sys/kernel/cap_last_cap" +diff -up util-linux-2.23.2/sys-utils/Makemodule.am.kzak util-linux-2.23.2/sys-utils/Makemodule.am +diff -up util-linux-2.23.2/sys-utils/nsenter.1.kzak util-linux-2.23.2/sys-utils/nsenter.1 +--- util-linux-2.23.2/sys-utils/nsenter.1.kzak 2015-06-26 09:58:39.468633643 +0200 ++++ util-linux-2.23.2/sys-utils/nsenter.1 2015-06-26 09:58:51.672541041 +0200 +@@ -1,44 +1,45 @@ +-.TH NSENTER 1 "January 2013" "util-linux" "User Commands" ++.TH NSENTER 1 "June 2013" "util-linux" "User Commands" + .SH NAME + nsenter \- run program with namespaces of other processes + .SH SYNOPSIS + .B nsenter +-.RI [ options ] +-.RI [ program ] +-.RI [ arguments ] ++[options] ++.RI [ program ++.RI [ arguments ]] + .SH DESCRIPTION + Enters the namespaces of one or more other processes and then executes the specified + program. Enterable namespaces are: + .TP + .B 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 + .RB ( CLONE_\:NEWNS +-flag), except for filesystems which are explicitly marked as shared (by mount +---make-\:shared). See /proc\:/self\:/mountinfo for the shared flag. ++flag), except for filesystems which are explicitly marked as shared (with ++\fBmount --make-\:shared\fP; see \fI/proc\:/self\:/mountinfo\fP for the ++\fBshared\fP flag). + .TP + .B UTS namespace +-setting hostname, domainname will not affect rest of the system ++Setting hostname or domainname will not affect the rest of the system. + .RB ( CLONE_\:NEWUTS +-flag). ++flag) + .TP + .B IPC namespace +-process will have independent namespace for System V message queues, semaphore +-sets and shared memory segments ++The process will have an independent namespace for System V message queues, ++semaphore sets and shared memory segments. + .RB ( CLONE_\:NEWIPC +-flag). ++flag) + .TP + .B network namespace +-process will have independent IPv4 and IPv6 stacks, IP routing tables, firewall +-rules, the ++The process will have independent IPv4 and IPv6 stacks, IP routing tables, ++firewall rules, the + .I /proc\:/net + and + .I /sys\:/class\:/net +-directory trees, sockets etc. ++directory trees, sockets, etc. + .RB ( CLONE_\:NEWNET +-flag). ++flag) + .TP + .B PID namespace +-children will have a set of PID to process mappings separate from the ++Children will have a set of PID to process mappings separate from the + .B nsenter + process + .RB ( CLONE_\:NEWPID +@@ -46,18 +47,18 @@ flag). + .B nsenter + will fork by default if changing the PID namespace, so that the new program + and its children share the same PID namespace and are visible to each other. +-If \-\-no\-fork is used, the new program will be exec'ed without forking. +-.PP +-See the +-.BR clone (2) +-for exact semantics of the flags. ++If \fB\-\-no\-fork\fP is used, the new program will be exec'ed without forking. + .TP +-If program is not given, run ``${SHELL}'' (default: /bin\:/sh). ++.B user namespace ++The process will have a distinct set of UIDs, GIDs and capabilities. ++.RB ( CLONE_\:NEWUSER ++flag) ++.TP ++See \fBclone\fP(2) for the exact semantics of the flags. ++.TP ++If \fIprogram\fP is not given, then ``${SHELL}'' is run (default: /bin\:/sh). + + .SH OPTIONS +-Argument with square brakets, such as [\fIfile\fR], means optional argument. +-Command line syntax to specify optional argument \-\-mount=/path\:/to\:/file. +-Please notice the equals sign. + .TP + \fB\-t\fR, \fB\-\-target\fR \fIpid\fP + Specify a target process to get contexts from. The paths to the contexts +@@ -83,6 +84,9 @@ the network namespace + /proc/\fIpid\fR/ns/pid + the PID namespace + .TP ++/proc/\fIpid\fR/ns/user ++the user namespace ++.TP + /proc/\fIpid\fR/root + the root directory + .TP +@@ -91,51 +95,71 @@ the working directory respectively + .PD + .RE + .TP +-\fB\-m\fR, \fB\-\-mount\fR [\fIfile\fR] +-Enter the mount namespace. If no file is specified enter the mount namespace +-of the target process. If file is specified enter the mount namespace ++\fB\-m\fR, \fB\-\-mount\fR[=\fIfile\fR] ++Enter the mount namespace. If no file is specified, enter the mount namespace ++of the target process. If file is specified, enter the mount namespace + specified by file. + .TP +-\fB\-u\fR, \fB\-\-uts\fR [\fIfile\fR] +-Enter the UTS namespace. If no file is specified enter the UTS namespace of +-the target process. If file is specified enter the UTS namespace specified by ++\fB\-u\fR, \fB\-\-uts\fR[=\fIfile\fR] ++Enter the UTS namespace. If no file is specified, enter the UTS namespace of ++the target process. If file is specified, enter the UTS namespace specified by + file. + .TP +-\fB\-i\fR, \fB\-\-ipc\fR [\fIfile\fR] +-Enter the IPC namespace. If no file is specified enter the IPC namespace of +-the target process. If file is specified enter the IPC namespace specified by ++\fB\-i\fR, \fB\-\-ipc\fR[=\fIfile\fR] ++Enter the IPC namespace. If no file is specified, enter the IPC namespace of ++the target process. If file is specified, enter the IPC namespace specified by + file. + .TP +-\fB\-n\fR, \fB\-\-net\fR [\fIfile\fR] +-Enter the network namespace. If no file is specified enter the network +-namespace of the target process. If file is specified enter the network ++\fB\-n\fR, \fB\-\-net\fR[=\fIfile\fR] ++Enter the network namespace. If no file is specified, enter the network ++namespace of the target process. If file is specified, enter the network + namespace specified by file. + .TP +-\fB\-p\fR, \fB\-\-pid\fR [\fIfile\fR] +-Enter the PID namespace. If no file is specified enter the PID namespace of +-the target process. If file is specified enter the PID namespace specified by ++\fB\-p\fR, \fB\-\-pid\fR[=\fIfile\fR] ++Enter the PID namespace. If no file is specified, enter the PID namespace of ++the target process. If file is specified, enter the PID namespace specified by + file. + .TP +-\fB\-r\fR, \fB\-\-root\fR [\fIdirectory\fR] +-Set the root directory. If no directory is specified set the root directory to +-the root directory of the target process. If directory is specified set the ++\fB\-U\fR, \fB\-\-user\fR[=\fIfile\fR] ++Enter the user namespace. If no file is specified, enter the user namespace of ++the target process. If file is specified, enter the user namespace specified by ++file. See also the \fB\-\-setuid\fR and \fB\-\-setgid\fR options. ++.TP ++\fB\-G\fR, \fB\-\-setgid\fR \fIgid\fR ++Set the group ID which will be used in the entered namespace and drop ++supplementary groups. ++.BR nsenter (1) ++always sets GID for user namespaces, the default is 0. ++.TP ++\fB\-S\fR, \fB\-\-setuid\fR \fIuid\fR ++Set the user ID which will be used in the entered namespace. ++.BR nsenter (1) ++always sets UID for user namespaces, the default is 0. ++.TP ++\fB\-\-preserve\-credentials\fR ++Don't modify UID and GID when enter user namespace. The default is to ++drops supplementary groups and sets GID and UID to 0. ++.TP ++\fB\-r\fR, \fB\-\-root\fR[=\fIdirectory\fR] ++Set the root directory. If no directory is specified, set the root directory to ++the root directory of the target process. If directory is specified, set the + root directory to the specified directory. + .TP +-\fB\-w\fR, \fB\-\-wd\fR [\fIdirectory\fR] +-Set the working directory. If no directory is specified set the working ++\fB\-w\fR, \fB\-\-wd\fR[=\fIdirectory\fR] ++Set the working directory. If no directory is specified, set the working + directory to the working directory of the target process. If directory is +-specified set the working directory to the specified directory. ++specified, set the working directory to the specified directory. + .TP +-\fB\-F\fR, \fB\-\-no-fork\fR +-Do not fork before exec'ing the specified program. By default when entering a +-pid namespace enter calls fork before calling exec so that the children will be +-in the newly entered pid namespace. ++\fB\-F\fR, \fB\-\-no\-fork\fR ++Do not fork before exec'ing the specified program. By default, when entering a ++PID namespace, \fBnsenter\fP calls \fBfork\fP before calling \fBexec\fP so that ++any children will also be in the newly entered PID namespace. + .TP + \fB\-V\fR, \fB\-\-version\fR + Display version information and exit. + .TP + \fB\-h\fR, \fB\-\-help\fR +-Print a help message. ++Display help text and exit. + .SH SEE ALSO + .BR setns (2), + .BR clone (2) +diff -up util-linux-2.23.2/sys-utils/nsenter.c.kzak util-linux-2.23.2/sys-utils/nsenter.c +--- util-linux-2.23.2/sys-utils/nsenter.c.kzak 2015-06-26 09:58:39.468633643 +0200 ++++ util-linux-2.23.2/sys-utils/nsenter.c 2015-06-26 09:58:51.673541033 +0200 +@@ -28,6 +28,7 @@ + #include <assert.h> + #include <sys/types.h> + #include <sys/wait.h> ++#include <grp.h> + + #include "strutils.h" + #include "nls.h" +@@ -42,7 +43,12 @@ static struct namespace_file { + int fd; + } namespace_files[] = { + /* Careful the order is significant in this array. ++ * ++ * The user namespace comes first, so that it is entered ++ * first. This gives an unprivileged user the potential to ++ * enter the other namespaces. + */ ++ { .nstype = CLONE_NEWUSER, .name = "ns/user", .fd = -1 }, + { .nstype = CLONE_NEWIPC, .name = "ns/ipc", .fd = -1 }, + { .nstype = CLONE_NEWUTS, .name = "ns/uts", .fd = -1 }, + { .nstype = CLONE_NEWNET, .name = "ns/net", .fd = -1 }, +@@ -56,18 +62,25 @@ static void usage(int status) + FILE *out = status == EXIT_SUCCESS ? stdout : stderr; + + fputs(USAGE_HEADER, out); +- fprintf(out, _(" %s [options] <program> [args...]\n"), ++ fprintf(out, _(" %s [options] <program> [<argument>...]\n"), + program_invocation_short_name); + ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Run a program with namespaces of other processes.\n"), out); ++ + fputs(USAGE_OPTIONS, out); + fputs(_(" -t, --target <pid> target process to get namespaces from\n"), out); +- fputs(_(" -m, --mount [=<file>] enter mount namespace\n"), out); +- fputs(_(" -u, --uts [=<file>] enter UTS namespace (hostname etc)\n"), out); +- fputs(_(" -i, --ipc [=<file>] enter System V IPC namespace\n"), out); +- fputs(_(" -n, --net [=<file>] enter network namespace\n"), out); +- fputs(_(" -p, --pid [=<file>] enter pid namespace\n"), out); +- fputs(_(" -r, --root [=<dir>] set the root directory\n"), out); +- fputs(_(" -w, --wd [=<dir>] set the working directory\n"), out); ++ fputs(_(" -m, --mount[=<file>] enter mount namespace\n"), out); ++ fputs(_(" -u, --uts[=<file>] enter UTS namespace (hostname etc)\n"), out); ++ fputs(_(" -i, --ipc[=<file>] enter System V IPC namespace\n"), out); ++ fputs(_(" -n, --net[=<file>] enter network namespace\n"), out); ++ fputs(_(" -p, --pid[=<file>] enter pid namespace\n"), out); ++ fputs(_(" -U, --user[=<file>] enter user namespace\n"), out); ++ fputs(_(" -S, --setuid <uid> set uid in entered namespace\n"), out); ++ fputs(_(" -G, --setgid <gid> set gid in entered namespace\n"), out); ++ fputs(_(" --preserve-credentials do not touch uids or gids\n"), out); ++ fputs(_(" -r, --root[=<dir>] set the root directory\n"), out); ++ fputs(_(" -w, --wd[=<dir>] set the working directory\n"), out); + fputs(_(" -F, --no-fork do not fork before exec'ing <program>\n"), out); + + fputs(USAGE_SEPARATOR, out); +@@ -153,6 +166,9 @@ static void continue_as_child(void) + + int main(int argc, char *argv[]) + { ++ enum { ++ OPT_PRESERVE_CRED = CHAR_MAX + 1 ++ }; + static const struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V'}, +@@ -162,24 +178,30 @@ int main(int argc, char *argv[]) + { "ipc", optional_argument, NULL, 'i' }, + { "net", optional_argument, NULL, 'n' }, + { "pid", optional_argument, NULL, 'p' }, ++ { "user", optional_argument, NULL, 'U' }, ++ { "setuid", required_argument, NULL, 'S' }, ++ { "setgid", required_argument, NULL, 'G' }, + { "root", optional_argument, NULL, 'r' }, + { "wd", optional_argument, NULL, 'w' }, + { "no-fork", no_argument, NULL, 'F' }, ++ { "preserve-credentials", no_argument, NULL, OPT_PRESERVE_CRED }, + { NULL, 0, NULL, 0 } + }; + + struct namespace_file *nsfile; +- int c, namespaces = 0; +- bool do_rd = false, do_wd = false; ++ int c, namespaces = 0, setgroups_nerrs = 0, preserve_cred = 0; ++ bool do_rd = false, do_wd = false, force_uid = false, force_gid = false; + int do_fork = -1; /* unknown yet */ ++ uid_t uid = 0; ++ gid_t gid = 0; + +- setlocale(LC_MESSAGES, ""); ++ setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + while ((c = +- getopt_long(argc, argv, "hVt:m::u::i::n::p::r::w::F", ++ getopt_long(argc, argv, "+hVt:m::u::i::n::p::U::S:G:r::w::F", + longopts, NULL)) != -1) { + switch (c) { + case 'h': +@@ -221,6 +243,20 @@ int main(int argc, char *argv[]) + else + namespaces |= CLONE_NEWPID; + break; ++ case 'U': ++ if (optarg) ++ open_namespace_fd(CLONE_NEWUSER, optarg); ++ else ++ namespaces |= CLONE_NEWUSER; ++ break; ++ case 'S': ++ uid = strtoul_or_err(optarg, _("failed to parse uid")); ++ force_uid = true; ++ break; ++ case 'G': ++ gid = strtoul_or_err(optarg, _("failed to parse gid")); ++ force_gid = true; ++ break; + case 'F': + do_fork = 0; + break; +@@ -236,6 +272,9 @@ int main(int argc, char *argv[]) + else + do_wd = true; + break; ++ case OPT_PRESERVE_CRED: ++ preserve_cred = 1; ++ break; + default: + usage(EXIT_FAILURE); + } +@@ -253,6 +292,26 @@ int main(int argc, char *argv[]) + open_target_fd(&wd_fd, "cwd", NULL); + + /* ++ * Update namespaces variable to contain all requested namespaces ++ */ ++ for (nsfile = namespace_files; nsfile->nstype; nsfile++) { ++ if (nsfile->fd < 0) ++ continue; ++ namespaces |= nsfile->nstype; ++ } ++ ++ /* for user namespaces we always set UID and GID (default is 0) ++ * and clear root's groups if --preserve-credentials is no specified */ ++ if ((namespaces & CLONE_NEWUSER) && !preserve_cred) { ++ force_uid = true, force_gid = true; ++ ++ /* We call setgroups() before and after we enter user namespace, ++ * let's complain only if both fail */ ++ if (setgroups(0, NULL) != 0) ++ setgroups_nerrs++; ++ } ++ ++ /* + * Now that we know which namespaces we want to enter, enter them. + */ + for (nsfile = namespace_files; nsfile->nstype; nsfile++) { +@@ -302,6 +361,15 @@ int main(int argc, char *argv[]) + if (do_fork == 1) + continue_as_child(); + ++ if (force_uid || force_gid) { ++ if (force_gid && setgroups(0, NULL) != 0 && setgroups_nerrs) /* drop supplementary groups */ ++ err(EXIT_FAILURE, _("setgroups failed")); ++ if (force_gid && setgid(gid) < 0) /* change GID */ ++ err(EXIT_FAILURE, _("setgid failed")); ++ if (force_uid && setuid(uid) < 0) /* change UID */ ++ err(EXIT_FAILURE, _("setuid failed")); ++ } ++ + if (optind < argc) { + execvp(argv[optind], argv + optind); + err(EXIT_FAILURE, _("failed to execute %s"), argv[optind]); +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 2015-06-26 09:58:39.484633521 +0200 ++++ util-linux-2.23.2/sys-utils/unshare.1 2015-06-26 09:58:51.673541033 +0200 +@@ -1,28 +1,27 @@ +-.\" Process this file with +-.\" groff -man -Tascii lscpu.1 +-.\" +-.TH UNSHARE 1 "July 2013" "util-linux" "User Commands" ++.TH UNSHARE 1 "July 2014" "util-linux" "User Commands" + .SH NAME + unshare \- run program with some namespaces unshared from parent + .SH SYNOPSIS + .B unshare +-.RI [ options ] ++[options] + .I program + .RI [ arguments ] + .SH DESCRIPTION + Unshares the indicated namespaces from the parent process and then executes +-the specified program. The namespaces to be unshared are indicated via ++the specified \fIprogram\fR. The namespaces to be unshared are indicated via + options. Unshareable namespaces are: + .TP + .BR "mount namespace" + 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 (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. ++shared (with \fBmount --make-shared\fP; see \fI/proc/self/mountinfo\fP or ++\fBfindmnt -o+PROPAGATION\fP for the \fBshared\fP flags). ++.sp ++.B unshare ++automatically sets propagation to \fBprivate\fP ++in the new mount namespace to make sure that the new namespace is really ++unshared. This feature is possible to disable by option \fB\-\-propagation unchanged\fP. ++Note that \fBprivate\fP is the kernel default. + .TP + .BR "UTS namespace" + Setting hostname or domainname will not affect the rest of the system. +@@ -40,13 +39,14 @@ sockets, etc. (\fBCLONE_NEWNET\fP flag) + .BR "pid namespace" + Children will have a distinct set of PID to process mappings from their parent. + (\fBCLONE_NEWPID\fP flag) ++.TP ++.BR "user namespace" ++The process will have a distinct set of UIDs, GIDs and capabilities. ++(\fBCLONE_NEWUSER\fP flag) + .PP + See \fBclone\fR(2) for the exact semantics of the flags. + .SH OPTIONS + .TP +-.BR \-h , " \-\-help" +-Display help text and exit. +-.TP + .BR \-i , " \-\-ipc" + Unshare the IPC namespace. + .TP +@@ -63,16 +63,68 @@ See also the \fB--fork\fP and \fB--mount + .BR \-u , " \-\-uts" + Unshare the UTS namespace. + .TP ++.BR \-U , " \-\-user" ++Unshare the user 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 ++.BR \-\-mount\-proc "[=\fImountpoint\fP]" ++Just before running the program, mount the proc filesystem at \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 ++mess up existing programs on the system. The new proc filesystem is explicitly + mounted as private (by MS_PRIVATE|MS_REC). ++.TP ++.BR \-r , " \-\-map\-root\-user" ++Run the program only after the current effective user and group IDs have been mapped to ++the superuser UID and GID in the newly created user namespace. This makes it possible to ++conveniently gain capabilities needed to manage various aspects of the newly created ++namespaces (such as configuring interfaces in the network namespace or mounting filesystems in ++the mount namespace) even when run unprivileged. As a mere convenience feature, it does not support ++more sophisticated use cases, such as mapping multiple ranges of UIDs and GIDs. ++This option implies --setgroups=deny. ++.TP ++.BR "\-\-propagation \fIprivate|shared|slave|unchanged\fP" ++Recursively sets mount propagation flag in the new mount namespace. The default ++is to set the propagation to \fIprivate\fP, this feature is possible to disable ++by \fIunchanged\fP argument. The options is silently ignored when mount namespace (\fB\-\-mount\fP) ++is not requested. ++.TP ++.BR "\-\-setgroups \fIallow|deny\fP" ++Allow or deny ++.BR setgroups (2) ++syscall in user namespaces. ++ ++.BR setgroups(2) ++is only callable with CAP_SETGID and CAP_SETGID in a user ++namespace (since Linux 3.19) does not give you permission to call setgroups(2) ++until after GID map has been set. The GID map is writable by root when ++.BR setgroups(2) ++is enabled and GID map becomes writable by unprivileged processes when ++.BR setgroups(2) ++is permanently disabled. ++.TP ++.BR \-V , " \-\-version" ++Display version information and exit. ++.TP ++.BR \-h , " \-\-help" ++Display help text and exit. ++.SH EXAMPLES ++.TP ++.B # unshare --fork --pid --mount-proc readlink /proc/self ++.TQ ++1 ++.br ++Establish a PID namespace, ensure we're PID 1 in it against newly mounted ++procfs instance. ++.TP ++.B $ unshare --map-root-user --user sh -c whoami ++.TQ ++root ++.br ++Establish a user namespace as an unprivileged user with a root user within it. + .SH SEE ALSO + .BR unshare (2), + .BR clone (2), +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 2015-06-26 09:58:39.484633521 +0200 ++++ util-linux-2.23.2/sys-utils/unshare.c 2015-06-26 09:58:51.673541033 +0200 +@@ -32,19 +32,117 @@ + + #include "nls.h" + #include "c.h" ++#include "closestream.h" + #include "namespace.h" + #include "exec_shell.h" + #include "xalloc.h" + #include "pathnames.h" ++#include "all-io.h" + ++/* 'private' is kernel default */ ++#define UNSHARE_PROPAGATION_DEFAULT (MS_REC | MS_PRIVATE) ++ ++enum { ++ SETGROUPS_NONE = -1, ++ SETGROUPS_DENY = 0, ++ SETGROUPS_ALLOW = 1, ++}; ++ ++static const char *setgroups_strings[] = ++{ ++ [SETGROUPS_DENY] = "deny", ++ [SETGROUPS_ALLOW] = "allow" ++}; ++ ++static int setgroups_str2id(const char *str) ++{ ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(setgroups_strings); i++) ++ if (strcmp(str, setgroups_strings[i]) == 0) ++ return i; ++ ++ errx(EXIT_FAILURE, _("unsupported --setgroups argument '%s'"), str); ++} ++ ++static void setgroups_control(int action) ++{ ++ const char *file = _PATH_PROC_SETGROUPS; ++ const char *cmd; ++ int fd; ++ ++ if (action < 0 || (size_t) action >= ARRAY_SIZE(setgroups_strings)) ++ return; ++ cmd = setgroups_strings[action]; ++ ++ fd = open(file, O_WRONLY); ++ if (fd < 0) { ++ if (errno == ENOENT) ++ return; ++ err(EXIT_FAILURE, _("cannot open %s"), file); ++ } ++ ++ if (write_all(fd, cmd, strlen(cmd))) ++ err(EXIT_FAILURE, _("write failed %s"), file); ++ close(fd); ++} ++ ++static void map_id(const char *file, uint32_t from, uint32_t to) ++{ ++ char *buf; ++ int fd; ++ ++ fd = open(file, O_WRONLY); ++ if (fd < 0) ++ err(EXIT_FAILURE, _("cannot open %s"), file); ++ ++ xasprintf(&buf, "%u %u 1", from, to); ++ if (write_all(fd, buf, strlen(buf))) ++ err(EXIT_FAILURE, _("write failed %s"), file); ++ free(buf); ++ close(fd); ++} ++ ++static unsigned long parse_propagation(const char *str) ++{ ++ size_t i; ++ static const struct prop_opts { ++ const char *name; ++ unsigned long flag; ++ } opts[] = { ++ { "slave", MS_REC | MS_SLAVE }, ++ { "private", MS_REC | MS_PRIVATE }, ++ { "shared", MS_REC | MS_SHARED }, ++ { "unchanged", 0 } ++ }; ++ ++ for (i = 0; i < ARRAY_SIZE(opts); i++) { ++ if (strcmp(opts[i].name, str) == 0) ++ return opts[i].flag; ++ } ++ ++ errx(EXIT_FAILURE, _("unsupported propagation mode: %s"), str); ++} ++ ++static void set_propagation(unsigned long flags) ++{ ++ if (flags == 0) ++ return; ++ ++ if (mount("none", "/", NULL, flags, NULL) != 0) ++ err(EXIT_FAILURE, _("cannot change root filesystem propagation")); ++} + + static void usage(int status) + { + FILE *out = status == EXIT_SUCCESS ? stdout : stderr; + + fputs(USAGE_HEADER, out); +- fprintf(out, +- _(" %s [options] <program> [args...]\n"), program_invocation_short_name); ++ fprintf(out, _(" %s [options] <program> [<argument>...]\n"), ++ program_invocation_short_name); ++ ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Run a program with some namespaces unshared from the parent.\n"), out); + + fputs(USAGE_OPTIONS, out); + fputs(_(" -m, --mount unshare mounts namespace\n"), out); +@@ -52,8 +150,13 @@ static void usage(int status) + 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(_(" -U, --user unshare user namespace\n"), out); + fputs(_(" -f, --fork fork before launching <program>\n"), out); + fputs(_(" --mount-proc[=<dir>] mount proc filesystem first (implies --mount)\n"), out); ++ fputs(_(" -r, --map-root-user map current user to root (implies --user)\n"), out); ++ fputs(_(" --propagation <slave|shared|private|unchanged>\n" ++ " modify mount propagation in mount namespace\n"), out); ++ fputs(_(" -s, --setgroups allow|deny control the setgroups syscall in user namespaces\n"), out); + + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); +@@ -66,7 +169,9 @@ static void usage(int status) + int main(int argc, char *argv[]) + { + enum { +- OPT_MOUNTPROC = CHAR_MAX + 1 ++ OPT_MOUNTPROC = CHAR_MAX + 1, ++ OPT_PROPAGATION, ++ OPT_SETGROUPS + }; + static const struct option longopts[] = { + { "help", no_argument, 0, 'h' }, +@@ -76,20 +181,29 @@ int main(int argc, char *argv[]) + { "ipc", no_argument, 0, 'i' }, + { "net", no_argument, 0, 'n' }, + { "pid", no_argument, 0, 'p' }, ++ { "user", no_argument, 0, 'U' }, + { "fork", no_argument, 0, 'f' }, + { "mount-proc", optional_argument, 0, OPT_MOUNTPROC }, ++ { "map-root-user", no_argument, 0, 'r' }, ++ { "propagation", required_argument, 0, OPT_PROPAGATION }, ++ { "setgroups", required_argument, 0, OPT_SETGROUPS }, + { NULL, 0, 0, 0 } + }; + ++ int setgrpcmd = SETGROUPS_NONE; + int unshare_flags = 0; +- int c, forkit = 0; ++ int c, forkit = 0, maproot = 0; + const char *procmnt = NULL; ++ unsigned long propagation = UNSHARE_PROPAGATION_DEFAULT; ++ uid_t real_euid = geteuid(); ++ gid_t real_egid = getegid();; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); ++ atexit(close_stdout); + +- while ((c = getopt_long(argc, argv, "+fhVmuinp", longopts, NULL)) != -1) { ++ while ((c = getopt_long(argc, argv, "+fhVmuinpUr", longopts, NULL)) != -1) { + switch (c) { + case 'f': + forkit = 1; +@@ -114,10 +228,23 @@ int main(int argc, char *argv[]) + case 'p': + unshare_flags |= CLONE_NEWPID; + break; ++ case 'U': ++ unshare_flags |= CLONE_NEWUSER; ++ break; + case OPT_MOUNTPROC: + unshare_flags |= CLONE_NEWNS; + procmnt = optarg ? optarg : "/proc"; + break; ++ case 'r': ++ unshare_flags |= CLONE_NEWUSER; ++ maproot = 1; ++ break; ++ case OPT_SETGROUPS: ++ setgrpcmd = setgroups_str2id(optarg); ++ break; ++ case OPT_PROPAGATION: ++ propagation = parse_propagation(optarg); ++ break; + default: + usage(EXIT_FAILURE); + } +@@ -146,6 +273,25 @@ int main(int argc, char *argv[]) + } + } + ++ if (maproot) { ++ if (setgrpcmd == SETGROUPS_ALLOW) ++ errx(EXIT_FAILURE, _("options --setgroups=allow and " ++ "--map-root-user are mutually exclusive")); ++ ++ /* since Linux 3.19 unprivileged writing of /proc/self/gid_map ++ * has s been disabled unless /proc/self/setgroups is written ++ * first to permanently disable the ability to call setgroups ++ * in that user namespace. */ ++ setgroups_control(SETGROUPS_DENY); ++ map_id(_PATH_PROC_UIDMAP, 0, real_euid); ++ map_id(_PATH_PROC_GIDMAP, 0, real_egid); ++ ++ } else if (setgrpcmd != SETGROUPS_NONE) ++ setgroups_control(setgrpcmd); ++ ++ if ((unshare_flags & CLONE_NEWNS) && propagation) ++ set_propagation(propagation); ++ + if (procmnt && + (mount("none", procmnt, NULL, MS_PRIVATE|MS_REC, NULL) != 0 || + mount("proc", procmnt, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)) diff --git a/SOURCES/2.27-libblkid-xfs-log.patch b/SOURCES/2.27-libblkid-xfs-log.patch new file mode 100644 index 0000000..190074f --- /dev/null +++ b/SOURCES/2.27-libblkid-xfs-log.patch @@ -0,0 +1,32 @@ +From d5b7d2912afceac3774d1aaea9e8486b54d4e9e9 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Tue, 13 Oct 2015 12:01:29 +0200 +Subject: [PATCH] libblkid: make XFS Log visible for wipefs + +Reported-by: Peter Rajnoha <prajnoha@redhat.com> +Signed-off-by: root <root@ws.net.home> +Signed-off-by: Karel Zak <kzak@redhat.com> +--- + libblkid/src/superblocks/xfs.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/libblkid/src/superblocks/xfs.c b/libblkid/src/superblocks/xfs.c +index a6c04a2..d13c849 100644 +--- a/libblkid/src/superblocks/xfs.c ++++ b/libblkid/src/superblocks/xfs.c +@@ -260,6 +260,12 @@ static int probe_xfs_log(blkid_probe pr, const struct blkid_idmag *mag) + + if (xlog_valid_rec_header(rhead)) { + blkid_probe_set_uuid_as(pr, rhead->h_uuid, "LOGUUID"); ++ ++ if (blkid_probe_set_magic(pr, i * 512, ++ sizeof(rhead->h_magicno), ++ (unsigned char *) &rhead->h_magicno)) ++ return 1; ++ + return 0; + } + } +-- +2.4.3 + diff --git a/SOURCES/2.28-lslogins-rebase.patch b/SOURCES/2.28-lslogins-rebase.patch new file mode 100644 index 0000000..fd0cdb4 --- /dev/null +++ b/SOURCES/2.28-lslogins-rebase.patch @@ -0,0 +1,460 @@ +diff -up util-linux-2.23.2/include/pathnames.h.kzak util-linux-2.23.2/include/pathnames.h +--- util-linux-2.23.2/include/pathnames.h.kzak 2016-03-16 15:17:42.648298525 +0100 ++++ util-linux-2.23.2/include/pathnames.h 2016-03-16 15:18:13.769055345 +0100 +@@ -36,6 +36,7 @@ + #endif + #define _PATH_MOTDFILE "/etc/motd" + #define _PATH_NOLOGIN "/etc/nologin" ++#define _PATH_VAR_NOLOGIN "/var/run/nologin" + + #define _PATH_LOGIN "/bin/login" + #define _PATH_INITTAB "/etc/inittab" +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 2016-03-16 15:17:42.639298595 +0100 ++++ util-linux-2.23.2/login-utils/lslogins.1 2016-03-16 15:18:13.769055345 +0100 +@@ -5,7 +5,10 @@ + 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] ++[options] ++.RB [ \-s | \-u [ =\fIUID ]] ++.RB [ \-g " \fIgroups\fR]" ++.RB [ \-l " \fIlogins\fR]" + .SH DESCRIPTION + .PP + Examine the wtmp and btmp logs, /etc/shadow (if necessary) and /etc/passwd +@@ -17,7 +20,7 @@ Mandatory arguments to long options are + .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.) ++date (see \fBshadow\fR(5) for more info). (Requires root privileges.) + .TP + \fB\-\-btmp\-file \fIpath\fP + Alternate path for btmp. +@@ -31,7 +34,7 @@ Output data in the format of NAME=VALUE. + \fB\-f\fR, \fB\-\-failed\fR + Display data about the users' last failed login attempts. + .TP +-\fB\-G\fR, \fB\-\-groups\-info\fR ++\fB\-G\fR, \fB\-\-supp\-groups\fR + Show information about groups. + .TP + \fB\-g\fR, \fB\-\-groups\fR=\fIgroups\fR +@@ -48,9 +51,6 @@ Display data containing information abou + 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 +@@ -71,21 +71,21 @@ Display information related to login by + \fB\-r\fR, \fB\-\-raw\fR + Raw output (no columnation). + .TP +-\fB\-s\fR, \fB\-\-system\-accs\fR[=\fIthreshold\fR] ++\fB\-s\fR, \fB\-\-system\-accs\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). ++(non-inclusive), with the exception of either nobody or nfsnobody (UID 65534). ++This hardcoded default maybe overwritten by parameters SYS_UID_MIN and SYS_UID_MAX in ++the file /etc/login.defs. + .TP +-\fB\-\-time-format\fR \fItype\fP ++\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] ++\fB\-u\fR, \fB\-\-user\-accs\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). ++(inclusive), with the exception of either nobody or nfsnobody (UID 65534). ++This hardcoded default maybe overwritten by parameters UID_MIN and UID_MAX in ++the file /etc/login.defs. + .TP + \fB\-V\fR, \fB\-\-version\fR + Display version information and exit. +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 2016-03-16 15:17:42.639298595 +0100 ++++ util-linux-2.23.2/login-utils/lslogins.c 2016-03-16 15:22:49.845899268 +0100 +@@ -144,6 +144,7 @@ enum { + TIME_SHORT, + TIME_FULL, + TIME_ISO, ++ TIME_ISO_SHORT, + }; + + /* +@@ -350,6 +351,9 @@ static char *make_time(int mode, time_t + case TIME_ISO: + strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", &tm); + break; ++ case TIME_ISO_SHORT: ++ strftime(buf, sizeof(buf), "%Y-%m-%d", &tm); ++ break; + default: + errx(EXIT_FAILURE, _("unsupported time type")); + } +@@ -396,7 +400,7 @@ again: + x = snprintf(p, len, "%s,", grp->gr_name); + } + +- if (x < 0 || (size_t) x + 1 > len) { ++ if (x < 0 || (size_t) x >= len) { + size_t cur = p - res; + + maxlen *= 2; +@@ -496,21 +500,24 @@ static int parse_btmp(struct lslogins_co + static int get_sgroups(gid_t **list, size_t *len, struct passwd *pwd) + { + size_t n = 0; ++ int ngroups = 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) ++ getgrouplist(pwd->pw_name, pwd->pw_gid, *list, &ngroups); ++ if (!ngroups) + return -1; + +- *list = xcalloc(1, *len * sizeof(gid_t)); ++ *list = xcalloc(1, ngroups * sizeof(gid_t)); + + /* now for the actual list of GIDs */ +- if (-1 == getgrouplist(pwd->pw_name, pwd->pw_gid, *list, (int *) len)) ++ if (-1 == getgrouplist(pwd->pw_name, pwd->pw_gid, *list, &ngroups)) + return -1; + ++ *len = (size_t) ngroups; ++ + /* getgroups also returns the user's primary GID - dispose of it */ + while (n < *len) { + if ((*list)[n] == pwd->pw_gid) +@@ -520,6 +527,7 @@ static int get_sgroups(gid_t **list, siz + + if (*len) + (*list)[n] = (*list)[--(*len)]; ++ + return 0; + } + +@@ -685,8 +693,8 @@ static struct lslogins_user *get_user_in + 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; ++ user->nologin = access(_PATH_NOLOGIN, F_OK) == 0 || ++ access(_PATH_VAR_NOLOGIN, F_OK) == 0; + break; + case COL_PWD_WARN: + if (shadow && shadow->sp_warn >= 0) +@@ -694,7 +702,8 @@ static struct lslogins_user *get_user_in + break; + case COL_PWD_EXPIR: + if (shadow && shadow->sp_expire >= 0) +- user->pwd_expire = make_time(TIME_SHORT, ++ user->pwd_expire = make_time(ctl->time_mode == TIME_ISO ? ++ TIME_ISO_SHORT : ctl->time_mode, + shadow->sp_expire * 86400); + break; + case COL_PWD_CTIME: +@@ -702,7 +711,8 @@ static struct lslogins_user *get_user_in + * (especially in non-GMT timezones) would only serve + * to confuse */ + if (shadow) +- user->pwd_ctime = make_time(TIME_SHORT, ++ user->pwd_ctime = make_time(ctl->time_mode == TIME_ISO ? ++ TIME_ISO_SHORT : ctl->time_mode, + shadow->sp_lstchg * 86400); + break; + case COL_PWD_CTIME_MIN: +@@ -852,7 +862,7 @@ static int get_user(struct lslogins_cont + const char *username) + { + *user = get_user_info(ctl, username); +- if (!*user && errno) ++ if (!*user) + if (IS_REAL_ERRNO(errno)) + return -1; + return 0; +@@ -887,33 +897,33 @@ static int create_usertree(struct lslogi + + static struct libscols_table *setup_table(struct lslogins_control *ctl) + { +- struct libscols_table *tb = scols_new_table(); ++ struct libscols_table *table = scols_new_table(); + int n = 0; + +- if (!tb) ++ if (!table) + errx(EXIT_FAILURE, _("failed to initialize output table")); + if (ctl->noheadings) +- scols_table_enable_noheadings(tb, 1); ++ scols_table_enable_noheadings(table, 1); + + switch(outmode) { + case OUT_COLON: +- scols_table_enable_raw(tb, 1); +- scols_table_set_column_separator(tb, ":"); ++ scols_table_enable_raw(table, 1); ++ scols_table_set_column_separator(table, ":"); + break; + case OUT_NEWLINE: +- scols_table_set_column_separator(tb, "\n"); ++ scols_table_set_column_separator(table, "\n"); + /* fallthrough */ + case OUT_EXPORT: +- scols_table_enable_export(tb, 1); ++ scols_table_enable_export(table, 1); + break; + case OUT_NUL: +- scols_table_set_line_separator(tb, "\0"); ++ scols_table_set_line_separator(table, "\0"); + /* fallthrough */ + case OUT_RAW: +- scols_table_enable_raw(tb, 1); ++ scols_table_enable_raw(table, 1); + break; + case OUT_PRETTY: +- scols_table_enable_noheadings(tb, 1); ++ scols_table_enable_noheadings(table, 1); + default: + break; + } +@@ -924,7 +934,7 @@ static struct libscols_table *setup_tabl + if (ctl->notrunc) + flags &= ~SCOLS_FL_TRUNC; + +- if (!scols_table_new_column(tb, ++ if (!scols_table_new_column(table, + coldescs[columns[n]].name, + coldescs[columns[n]].whint, + flags)) +@@ -932,9 +942,9 @@ static struct libscols_table *setup_tabl + ++n; + } + +- return tb; ++ return table; + fail: +- scols_unref_table(tb); ++ scols_unref_table(table); + return NULL; + } + +@@ -1050,10 +1060,10 @@ static void fill_table(const void *u, co + return; + } + #ifdef HAVE_LIBSYSTEMD +-static void print_journal_tail(const char *journal_path, uid_t uid, size_t len) ++static void print_journal_tail(const char *journal_path, uid_t uid, size_t len, int time_mode) + { + sd_journal *j; +- char *match, *buf; ++ char *match, *timestamp; + uint64_t x; + time_t t; + const char *identifier, *pid, *message; +@@ -1064,7 +1074,6 @@ static void print_journal_tail(const cha + 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); +@@ -1074,37 +1083,35 @@ static void print_journal_tail(const cha + do { + if (0 > sd_journal_get_data(j, "SYSLOG_IDENTIFIER", + (const void **) &identifier, &identifier_len)) +- return; ++ goto done; + if (0 > sd_journal_get_data(j, "_PID", + (const void **) &pid, &pid_len)) +- return; ++ goto done; + if (0 > sd_journal_get_data(j, "MESSAGE", + (const void **) &message, &message_len)) +- return; ++ goto done; + + sd_journal_get_realtime_usec(j, &x); + t = x / 1000000; +- strftime(buf, 16, "%b %d %H:%M:%S", localtime(&t)); +- +- fprintf(stdout, "%s", buf); +- ++ timestamp = make_time(time_mode, t); ++ /* Get rid of journal entry field identifiers */ + identifier = strchr(identifier, '=') + 1; +- pid = strchr(pid, '=') + 1 ; ++ pid = strchr(pid, '=') + 1; + message = strchr(message, '=') + 1; + +- fprintf(stdout, " %s", identifier); +- fprintf(stdout, "[%s]:", pid); +- fprintf(stdout, "%s\n", message); ++ fprintf(stdout, "%s %s[%s]: %s\n", timestamp, identifier, pid, ++ message); ++ free(timestamp); + } while (sd_journal_next(j)); + +- free(buf); ++done: + free(match); + sd_journal_flush_matches(j); + sd_journal_close(j); + } + #endif + +-static int print_pretty(struct libscols_table *tb) ++static int print_pretty(struct libscols_table *table) + { + struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); + struct libscols_column *col; +@@ -1113,8 +1120,8 @@ static int print_pretty(struct libscols_ + const char *hstr, *dstr; + int n = 0; + +- ln = scols_table_get_line(tb, 0); +- while (!scols_table_next_column(tb, itr, &col)) { ++ ln = scols_table_get_line(table, 0); ++ while (!scols_table_next_column(table, itr, &col)) { + + data = scols_line_get_cell(ln, n); + +@@ -1142,7 +1149,7 @@ static int print_user_table(struct lslog + print_pretty(tb); + #ifdef HAVE_LIBSYSTEMD + fprintf(stdout, _("\nLast logs:\n")); +- print_journal_tail(ctl->journal_path, ctl->uid, 3); ++ print_journal_tail(ctl->journal_path, ctl->uid, 3, ctl->time_mode); + fputc('\n', stdout); + #endif + } else +@@ -1175,16 +1182,25 @@ static void free_user(void *f) + free(u); + } + +-struct lslogins_timefmt { +- const char *name; +- int val; +-}; ++static int parse_time_mode(const char *optarg) ++{ ++ struct lslogins_timefmt { ++ const char *name; ++ const int val; ++ }; ++ static const struct lslogins_timefmt timefmts[] = { ++ {"iso", TIME_ISO}, ++ {"full", TIME_FULL}, ++ {"short", TIME_SHORT}, ++ }; ++ size_t i; + +-static struct lslogins_timefmt timefmts[] = { +- { "short", TIME_SHORT }, +- { "full", TIME_FULL }, +- { "iso", TIME_ISO }, +-}; ++ for (i = 0; i < ARRAY_SIZE(timefmts); i++) { ++ if (strcmp(timefmts[i].name, optarg) == 0) ++ return timefmts[i].val; ++ } ++ errx(EXIT_FAILURE, _("unknown time format: %s"), optarg); ++} + + static void __attribute__((__noreturn__)) usage(FILE *out) + { +@@ -1193,16 +1209,18 @@ static void __attribute__((__noreturn__) + fputs(USAGE_HEADER, out); + fprintf(out, _(" %s [options]\n"), program_invocation_short_name); + ++ fputs(USAGE_SEPARATOR, out); ++ fputs(_("Display information about known users in the system.\n"), out); ++ + 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, --supp-groups display information about groups\n"), out); + fputs(_(" -g, --groups=<groups> display users belonging to a group in <groups>\n"), out); + fputs(_(" -L, --last show info about the users' last login sessions\n"), out); + fputs(_(" -l, --logins=<logins> display only users from <logins>\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); +@@ -1226,7 +1244,7 @@ static void __attribute__((__noreturn__) + fprintf(out, " %14s %s\n", coldescs[i].name, + _(coldescs[i].help)); + +- fprintf(out, _("\nFor more details see lslogins(1).\n")); ++ fprintf(out, USAGE_MAN_TAIL("lslogins(1)")); + + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); + } +@@ -1241,8 +1259,7 @@ int main(int argc, char *argv[]) + + /* long only options. */ + enum { +- OPT_VER = CHAR_MAX + 1, +- OPT_WTMP, ++ OPT_WTMP = CHAR_MAX + 1, + OPT_BTMP, + OPT_NOTRUNC, + OPT_NOHEAD, +@@ -1300,7 +1317,7 @@ int main(int argc, char *argv[]) + add_column(columns, ncolumns++, COL_UID); + add_column(columns, ncolumns++, COL_USER); + +- while ((c = getopt_long(argc, argv, "acfGg:hLl:no:prsuVxzZ", ++ while ((c = getopt_long(argc, argv, "acefGg:hLl:no:prsuVzZ", + longopts, NULL)) != -1) { + + err_exclusive_options(c, longopts, excl, excl_st); +@@ -1394,18 +1411,7 @@ int main(int argc, char *argv[]) + 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); +- } ++ ctl->time_mode = parse_time_mode(optarg); + break; + case 'V': + printf(UTIL_LINUX_VERSION); +@@ -1433,7 +1439,7 @@ int main(int argc, char *argv[]) + logins = argv[optind]; + outmode = OUT_PRETTY; + } else if (argc != optind) +- usage(stderr); ++ errx(EXIT_FAILURE, _("Only one user may be specified. Use -l for multiple users.")); + + scols_init_debug(0); + diff --git a/SOURCES/nologin.8 b/SOURCES/nologin.8 new file mode 100644 index 0000000..5cb1601 --- /dev/null +++ b/SOURCES/nologin.8 @@ -0,0 +1,63 @@ +.\" $OpenBSD: nologin.8,v 1.8 1999/06/04 02:45:19 aaron Exp $ +.\" $NetBSD: nologin.8,v 1.3 1995/03/18 14:59:09 cgd Exp $ +.\" +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)nologin.8 8.1 (Berkeley) 6/19/93 +.\" +.Dd February 15, 1997 +.Dt NOLOGIN 8 +.Os +.Sh NAME +.Nm nologin +.Nd politely refuse a login +.Sh SYNOPSIS +.Nm nologin +.Sh DESCRIPTION +.Nm +displays a message that an account is not available and +exits non-zero. +It is intended as a replacement shell field for accounts that +have been disabled. +.Pp +If the file +.Pa /etc/nologin.txt +exists, +.Nm +displays its contents to the user instead of the default message. +.Sh SEE ALSO +.Xr login 1 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.4 . diff --git a/SOURCES/nologin.c b/SOURCES/nologin.c new file mode 100644 index 0000000..8a51ba9 --- /dev/null +++ b/SOURCES/nologin.c @@ -0,0 +1,58 @@ +/* $OpenBSD: nologin.c,v 1.2 1997/04/04 16:51:37 millert Exp $ */ + +/* + * Copyright (c) 1997, Jason Downs. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +/* Distinctly different from _PATH_NOLOGIN. */ +#define _PATH_NOLOGIN_TXT "/etc/nologin.txt" + +#define DEFAULT_MESG "This account is currently not available.\n" + +/*ARGSUSED*/ +int main(argc, argv) + int argc; + char *argv[]; +{ + int nfd, nrd; + char nbuf[128]; + + nfd = open(_PATH_NOLOGIN_TXT, O_RDONLY); + if (nfd < 0) { + write(STDOUT_FILENO, DEFAULT_MESG, strlen(DEFAULT_MESG)); + exit (1); + } + + while ((nrd = read(nfd, nbuf, sizeof(nbuf))) > 0) + write(STDOUT_FILENO, nbuf, nrd); + close (nfd); + + exit (1); +} diff --git a/SOURCES/rhel7.0-unshare-user.patch b/SOURCES/rhel7.0-unshare-user.patch new file mode 100644 index 0000000..56ec755 --- /dev/null +++ b/SOURCES/rhel7.0-unshare-user.patch @@ -0,0 +1,154 @@ +diff -up util-linux-2.23.2/sys-utils/nsenter.1.kzak util-linux-2.23.2/sys-utils/nsenter.1 +--- util-linux-2.23.2/sys-utils/nsenter.1.kzak 2014-03-12 12:39:19.283577293 +0100 ++++ util-linux-2.23.2/sys-utils/nsenter.1 2014-03-12 12:42:08.930336415 +0100 +@@ -47,12 +47,7 @@ flag). + will fork by default if changing the PID namespace, so that the new program + and its children share the same PID namespace and are visible to each other. + If \-\-no\-fork is used, the new program will be exec'ed without forking. +-.TP +-.B user namespace +-process will have distinct set of UIDs, GIDs and capabilities +-.RB ( CLONE_\:NEWUSER +-flag). +-.TP ++.PP + See the + .BR clone (2) + for exact semantics of the flags. +@@ -88,9 +83,6 @@ the network namespace + /proc/\fIpid\fR/ns/pid + the PID namespace + .TP +-/proc/\fIpid\fR/ns/user +-the user namespace +-.TP + /proc/\fIpid\fR/root + the root directory + .TP +@@ -124,11 +116,6 @@ Enter the PID namespace. If no file is + the target process. If file is specified enter the PID namespace specified by + file. + .TP +-\fB\-U\fR, \fB\-\-user\fR [\fIfile\fR] +-Enter the user namespace. If no file is specified enter the user namespace of +-the target process. If file is specified enter the user namespace specified by +-file. +-.TP + \fB\-r\fR, \fB\-\-root\fR [\fIdirectory\fR] + Set the root directory. If no directory is specified set the root directory to + the root directory of the target process. If directory is specified set the +diff -up util-linux-2.23.2/sys-utils/nsenter.c.kzak util-linux-2.23.2/sys-utils/nsenter.c +--- util-linux-2.23.2/sys-utils/nsenter.c.kzak 2014-03-12 12:39:10.402485179 +0100 ++++ util-linux-2.23.2/sys-utils/nsenter.c 2014-03-12 12:44:07.986570461 +0100 +@@ -42,12 +42,7 @@ static struct namespace_file { + int fd; + } namespace_files[] = { + /* Careful the order is significant in this array. +- * +- * The user namespace comes first, so that it is entered +- * first. This gives an unprivileged user the potential to +- * enter the other namespaces. + */ +- { .nstype = CLONE_NEWUSER, .name = "ns/user", .fd = -1 }, + { .nstype = CLONE_NEWIPC, .name = "ns/ipc", .fd = -1 }, + { .nstype = CLONE_NEWUTS, .name = "ns/uts", .fd = -1 }, + { .nstype = CLONE_NEWNET, .name = "ns/net", .fd = -1 }, +@@ -71,7 +66,6 @@ static void usage(int status) + fputs(_(" -i, --ipc [=<file>] enter System V IPC namespace\n"), out); + fputs(_(" -n, --net [=<file>] enter network namespace\n"), out); + fputs(_(" -p, --pid [=<file>] enter pid namespace\n"), out); +- fputs(_(" -U, --user [=<file>] enter user namespace\n"), out); + fputs(_(" -r, --root [=<dir>] set the root directory\n"), out); + fputs(_(" -w, --wd [=<dir>] set the working directory\n"), out); + fputs(_(" -F, --no-fork do not fork before exec'ing <program>\n"), out); +@@ -168,7 +162,6 @@ int main(int argc, char *argv[]) + { "ipc", optional_argument, NULL, 'i' }, + { "net", optional_argument, NULL, 'n' }, + { "pid", optional_argument, NULL, 'p' }, +- { "user", optional_argument, NULL, 'U' }, + { "root", optional_argument, NULL, 'r' }, + { "wd", optional_argument, NULL, 'w' }, + { "no-fork", no_argument, NULL, 'F' }, +@@ -186,7 +179,7 @@ int main(int argc, char *argv[]) + atexit(close_stdout); + + while ((c = +- getopt_long(argc, argv, "hVt:m::u::i::n::p::U::r::w::F", ++ getopt_long(argc, argv, "hVt:m::u::i::n::p::r::w::F", + longopts, NULL)) != -1) { + switch (c) { + case 'h': +@@ -228,12 +221,6 @@ int main(int argc, char *argv[]) + else + namespaces |= CLONE_NEWPID; + break; +- case 'U': +- if (optarg) +- open_namespace_fd(CLONE_NEWUSER, optarg); +- else +- namespaces |= CLONE_NEWUSER; +- break; + case 'F': + do_fork = 0; + break; +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-03-12 12:39:41.367806340 +0100 ++++ util-linux-2.23.2/sys-utils/unshare.1 2014-03-12 12:40:25.186260760 +0100 +@@ -34,9 +34,6 @@ etc. (\fBCLONE_NEWNET\fP flag). + .BR "pid namespace" + children will have a distinct set of pid to process mappings than their parent. + (\fBCLONE_NEWPID\fP flag). +-.TP +-.BR "user namespace" +-process will have distinct set of uids, gids and capabilities. (\fBCLONE_NEWUSER\fP flag). + .PP + See the \fBclone\fR(2) for exact semantics of the flags. + .SH OPTIONS +@@ -58,9 +55,6 @@ Unshare the network namespace. + .TP + .BR \-p , " \-\-pid" + Unshare the pid namespace. +-.TP +-.BR \-U , " \-\-user" +-Unshare the user namespace. + .SH SEE ALSO + .BR unshare (2), + .BR clone (2) +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-03-12 12:39:46.385858383 +0100 ++++ util-linux-2.23.2/sys-utils/unshare.c 2014-03-12 12:44:49.955005384 +0100 +@@ -45,7 +45,6 @@ static void usage(int status) + 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(_(" -U, --user unshare user namespace\n"), out); + + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); +@@ -65,7 +64,6 @@ int main(int argc, char *argv[]) + { "ipc", no_argument, 0, 'i' }, + { "net", no_argument, 0, 'n' }, + { "pid", no_argument, 0, 'p' }, +- { "user", no_argument, 0, 'U' }, + { NULL, 0, 0, 0 } + }; + +@@ -78,7 +76,7 @@ int main(int argc, char *argv[]) + textdomain(PACKAGE); + atexit(close_stdout); + +- while ((c = getopt_long(argc, argv, "hVmuinpU", longopts, NULL)) != -1) { ++ while ((c = getopt_long(argc, argv, "hVmuinp", longopts, NULL)) != -1) { + switch (c) { + case 'h': + usage(EXIT_SUCCESS); +@@ -100,9 +98,6 @@ int main(int argc, char *argv[]) + case 'p': + unshare_flags |= CLONE_NEWPID; + break; +- case 'U': +- unshare_flags |= CLONE_NEWUSER; +- break; + default: + usage(EXIT_FAILURE); + } diff --git a/SOURCES/util-linux-60-raw.rules b/SOURCES/util-linux-60-raw.rules new file mode 100644 index 0000000..abbf79d --- /dev/null +++ b/SOURCES/util-linux-60-raw.rules @@ -0,0 +1,8 @@ +# +# Enter raw device bindings here. +# +# An example would be: +# ACTION=="add", KERNEL=="sda", RUN+="/usr/bin/raw /dev/raw/raw1 %N" +# to bind /dev/raw/raw1 to /dev/sda, or +# ACTION=="add", ENV{MAJOR}=="8", ENV{MINOR}=="1", RUN+="/usr/bin/raw /dev/raw/raw2 %M %m" +# to bind /dev/raw/raw2 to the device with major 8, minor 1. diff --git a/SOURCES/util-linux-chsh-chfn.pamd b/SOURCES/util-linux-chsh-chfn.pamd new file mode 100644 index 0000000..2dbc0aa --- /dev/null +++ b/SOURCES/util-linux-chsh-chfn.pamd @@ -0,0 +1,6 @@ +#%PAM-1.0 +auth sufficient pam_rootok.so +auth include system-auth +account include system-auth +password include system-auth +session include system-auth diff --git a/SOURCES/util-linux-login.pamd b/SOURCES/util-linux-login.pamd new file mode 100644 index 0000000..3c03927 --- /dev/null +++ b/SOURCES/util-linux-login.pamd @@ -0,0 +1,18 @@ +#%PAM-1.0 +auth [user_unknown=ignore success=ok ignore=ignore default=bad] pam_securetty.so +auth substack system-auth +auth include postlogin +account required pam_nologin.so +account include system-auth +password include system-auth +# pam_selinux.so close should be the first session rule +session required pam_selinux.so close +session required pam_loginuid.so +session optional pam_console.so +# pam_selinux.so open should only be followed by sessions to be executed in the user context +session required pam_selinux.so open +session required pam_namespace.so +session optional pam_keyinit.so force revoke +session include system-auth +session include postlogin +-session optional pam_ck_connector.so diff --git a/SOURCES/util-linux-remote.pamd b/SOURCES/util-linux-remote.pamd new file mode 100644 index 0000000..2f22cc2 --- /dev/null +++ b/SOURCES/util-linux-remote.pamd @@ -0,0 +1,16 @@ +#%PAM-1.0 +auth required pam_securetty.so +auth substack password-auth +auth include postlogin +account required pam_nologin.so +account include password-auth +password include password-auth +# pam_selinux.so close should be the first session rule +session required pam_selinux.so close +session required pam_loginuid.so +# pam_selinux.so open should only be followed by sessions to be executed in the user context +session required pam_selinux.so open +session required pam_namespace.so +session optional pam_keyinit.so force revoke +session include password-auth +session include postlogin diff --git a/SOURCES/util-linux-runuser-l.pamd b/SOURCES/util-linux-runuser-l.pamd new file mode 100644 index 0000000..7a9a48c --- /dev/null +++ b/SOURCES/util-linux-runuser-l.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth include runuser +session optional pam_keyinit.so force revoke +-session optional pam_systemd.so +session include runuser diff --git a/SOURCES/util-linux-runuser.pamd b/SOURCES/util-linux-runuser.pamd new file mode 100644 index 0000000..37f0e84 --- /dev/null +++ b/SOURCES/util-linux-runuser.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth sufficient pam_rootok.so +session optional pam_keyinit.so revoke +session required pam_limits.so +session required pam_unix.so diff --git a/SOURCES/util-linux-su-l.pamd b/SOURCES/util-linux-su-l.pamd new file mode 100644 index 0000000..656a139 --- /dev/null +++ b/SOURCES/util-linux-su-l.pamd @@ -0,0 +1,6 @@ +#%PAM-1.0 +auth include su +account include su +password include su +session optional pam_keyinit.so force revoke +session include su diff --git a/SOURCES/util-linux-su.pamd b/SOURCES/util-linux-su.pamd new file mode 100644 index 0000000..030657f --- /dev/null +++ b/SOURCES/util-linux-su.pamd @@ -0,0 +1,14 @@ +#%PAM-1.0 +auth sufficient pam_rootok.so +# Uncomment the following line to implicitly trust users in the "wheel" group. +#auth sufficient pam_wheel.so trust use_uid +# Uncomment the following line to require a user to be in the "wheel" group. +#auth required pam_wheel.so use_uid +auth substack system-auth +auth include postlogin +account sufficient pam_succeed_if.so uid = 0 use_uid quiet +account include system-auth +password include system-auth +session include system-auth +session include postlogin +session optional pam_xauth.so diff --git a/SOURCES/v2.26-nsenter-selinux.patch b/SOURCES/v2.26-nsenter-selinux.patch new file mode 100644 index 0000000..0676127 --- /dev/null +++ b/SOURCES/v2.26-nsenter-selinux.patch @@ -0,0 +1,129 @@ +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 2015-06-26 10:21:34.337221288 +0200 ++++ util-linux-2.23.2/sys-utils/Makemodule.am 2015-06-26 10:22:18.719885983 +0200 +@@ -308,7 +308,7 @@ if BUILD_NSENTER + usrbin_exec_PROGRAMS += nsenter + dist_man_MANS += sys-utils/nsenter.1 + nsenter_SOURCES = sys-utils/nsenter.c +-nsenter_LDADD = $(LDADD) libcommon.la ++nsenter_LDADD = $(LDADD) libcommon.la $(SELINUX_LIBS) + endif + + if BUILD_HWCLOCK +diff -up util-linux-2.23.2/sys-utils/nsenter.1.kzak util-linux-2.23.2/sys-utils/nsenter.1 +--- util-linux-2.23.2/sys-utils/nsenter.1.kzak 2015-06-26 10:14:00.947646586 +0200 ++++ util-linux-2.23.2/sys-utils/nsenter.1 2015-06-26 10:21:34.337221288 +0200 +@@ -155,6 +155,11 @@ Do not fork before exec'ing the specifie + PID namespace, \fBnsenter\fP calls \fBfork\fP before calling \fBexec\fP so that + any children will also be in the newly entered PID namespace. + .TP ++\fB\-Z\fR, \fB\-\-follow\-context\fR ++Set the SELinux security context used for executing a new process according to ++already running process specified by \fB\-\-target\fR PID. (The util-linux has ++to be compiled with SELinux support otherwise the option is unavailable.) ++.TP + \fB\-V\fR, \fB\-\-version\fR + Display version information and exit. + .TP +@@ -163,10 +168,14 @@ Display help text and exit. + .SH SEE ALSO + .BR setns (2), + .BR clone (2) +-.SH AUTHOR +-.MT ebiederm@xmission.com ++.SH AUTHORS ++.UR biederm@xmission.com + Eric Biederman +-.ME ++.UE ++.br ++.UR kzak@redhat.com ++Karel Zak ++.UE + .SH AVAILABILITY + The nsenter command is part of the util-linux package and is available from + .UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/ +diff -up util-linux-2.23.2/sys-utils/nsenter.c.kzak util-linux-2.23.2/sys-utils/nsenter.c +--- util-linux-2.23.2/sys-utils/nsenter.c.kzak 2015-06-26 10:14:00.947646586 +0200 ++++ util-linux-2.23.2/sys-utils/nsenter.c 2015-06-26 10:21:34.337221288 +0200 +@@ -30,6 +30,10 @@ + #include <sys/wait.h> + #include <grp.h> + ++#ifdef HAVE_LIBSELINUX ++# include <selinux/selinux.h> ++#endif ++ + #include "strutils.h" + #include "nls.h" + #include "c.h" +@@ -82,6 +86,9 @@ static void usage(int status) + fputs(_(" -r, --root[=<dir>] set the root directory\n"), out); + fputs(_(" -w, --wd[=<dir>] set the working directory\n"), out); + fputs(_(" -F, --no-fork do not fork before exec'ing <program>\n"), out); ++#ifdef HAVE_LIBSELINUX ++ fputs(_(" -Z, --follow-context set SELinux context according to --target PID\n"), out); ++#endif + + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); +@@ -185,6 +192,9 @@ int main(int argc, char *argv[]) + { "wd", optional_argument, NULL, 'w' }, + { "no-fork", no_argument, NULL, 'F' }, + { "preserve-credentials", no_argument, NULL, OPT_PRESERVE_CRED }, ++#ifdef HAVE_LIBSELINUX ++ { "follow-context", no_argument, NULL, 'Z' }, ++#endif + { NULL, 0, NULL, 0 } + }; + +@@ -194,6 +204,9 @@ int main(int argc, char *argv[]) + int do_fork = -1; /* unknown yet */ + uid_t uid = 0; + gid_t gid = 0; ++#ifdef HAVE_LIBSELINUX ++ bool selinux = 0; ++#endif + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); +@@ -201,7 +214,7 @@ int main(int argc, char *argv[]) + atexit(close_stdout); + + while ((c = +- getopt_long(argc, argv, "+hVt:m::u::i::n::p::U::S:G:r::w::F", ++ getopt_long(argc, argv, "+hVt:m::u::i::n::p::U::S:G:r::w::FZ", + longopts, NULL)) != -1) { + switch (c) { + case 'h': +@@ -275,11 +288,30 @@ int main(int argc, char *argv[]) + case OPT_PRESERVE_CRED: + preserve_cred = 1; + break; ++#ifdef HAVE_LIBSELINUX ++ case 'Z': ++ selinux = 1; ++ break; ++#endif + default: + usage(EXIT_FAILURE); + } + } + ++#ifdef HAVE_LIBSELINUX ++ if (selinux && is_selinux_enabled() > 0) { ++ char *scon = NULL; ++ ++ if (!namespace_target_pid) ++ errx(EXIT_FAILURE, _("no target PID specified for --follow-context")); ++ if (getpidcon(namespace_target_pid, &scon) < 0) ++ errx(EXIT_FAILURE, _("failed to get %d SELinux context"), ++ (int) namespace_target_pid); ++ if (setexeccon(scon) < 0) ++ errx(EXIT_FAILURE, _("failed to set exec context to '%s'"), scon); ++ freecon(scon); ++ } ++#endif + /* + * Open remaining namespace and directory descriptors. + */ diff --git a/SPECS/util-linux.spec b/SPECS/util-linux.spec new file mode 100644 index 0000000..761ccf3 --- /dev/null +++ b/SPECS/util-linux.spec @@ -0,0 +1,3284 @@ +### Header +Summary: A collection of basic system utilities +Name: util-linux +Version: 2.23.2 +Release: 63%{?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 + +%define upstream_version %{version} + +### Macros +%define cytune_archs %{ix86} alpha %{arm} +%define compldir %{_datadir}/bash-completion/completions/ + +### Dependencies +BuildRequires: audit-libs-devel >= 1.0.6 +BuildRequires: gettext-devel +BuildRequires: libselinux-devel +BuildRequires: ncurses-devel +BuildRequires: pam-devel +BuildRequires: zlib-devel +BuildRequires: popt-devel +BuildRequires: libutempter-devel +Buildrequires: systemd-devel +Buildrequires: libuser-devel +BuildRequires: libcap-ng-devel +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: libtool +%ifarch ppc64le +BuildRequires: librtas-devel +%endif + +### Sources +Source0: ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.23/util-linux-%{upstream_version}.tar.xz +Source1: util-linux-login.pamd +Source2: util-linux-remote.pamd +Source3: util-linux-chsh-chfn.pamd +Source4: util-linux-60-raw.rules +Source8: nologin.c +Source9: nologin.8 +Source12: util-linux-su.pamd +Source13: util-linux-su-l.pamd +Source14: util-linux-runuser.pamd +Source15: util-linux-runuser-l.pamd + +### Obsoletes & Conflicts & Provides +Conflicts: bash-completion < 1:2.1-1 +# su(1) and runuser(1) merged into util-linux v2.22 +Conflicts: coreutils < 8.20 +# eject has been merged into util-linux v2.22 +Obsoletes: eject <= 2.1.5 +Provides: eject = 2.1.6 +# sulogin, utmpdump merged into util-linux v2.22 +Conflicts: sysvinit-tools < 2.88-8 +# old versions of e2fsprogs contain fsck, uuidgen +Conflicts: e2fsprogs < 1.41.8-5 +# rename from util-linux-ng back to util-linux +Obsoletes: util-linux-ng < 2.19 +Provides: util-linux-ng = %{version}-%{release} +Conflicts: filesystem < 3 +# lsmem and chmem from s390utils +Conflicts: s390utils-base < 2:1.23.0-33 + +Provides: /bin/dmesg +Provides: /bin/kill +Provides: /bin/more +Provides: /bin/mount +Provides: /bin/umount +Provides: /sbin/blkid +Provides: /sbin/blockdev +Provides: /sbin/findfs +Provides: /sbin/fsck +Provides: /sbin/nologin + +Requires(post): coreutils +Requires: pam >= 1.1.3-7, /etc/pam.d/system-auth +Requires: audit-libs >= 1.0.6 +Requires: libuuid = %{version}-%{release} +Requires: libblkid = %{version}-%{release} +Requires: libmount = %{version}-%{release} +Requires: libsmartcols = %{version}-%{release} + +### Ready for upstream? +### +# 151635 - makeing /var/log/lastlog +Patch0: 2.23-login-lastlog-create.patch + +# v2.24 backport: #972457 +Patch1: 2.24-agetty-clocal.patch +# v2.24 backport: #987787 - Remove lastlogin from su +Patch2: 2.24-su-suppress-PAM-info-messages.patch +# v2.24 backport: #950497 - problem umounting loop device +Patch3: 2.24-libmount-canonicalize-for-conversion-from-loopdev.patch +# v2.24 backport: #921498 - multiple internal testsuite failures +Patch4: 2.24-tests-portability.patch +# v2.24 backport: #1005566 - recount_geometry: Process /usr/sbin/fdisk was killed by signal 8 (SIGFPE) +Patch5: 2.24-libfdisk-fix-SIGFPE.patch +# v2.24 backport: #1005194 - su generates incorrect log entries +Patch6: 2.24-su-fix-lastlog-and-btmp-logging.patch +# v2.24 backport: #1006462 - RFE: allow \S escape that defaults to PRETTY_NAME in /etc/issue handling +Patch7: 2.24-agetty-etc-os-release.patch +# v2.24 backport: #1010193 - libmount umount issues +Patch8: 2.24-libmount-mem.patch +# v2.24 backport: #1009349 - Joking sfdisk rewriting PT after "n" +Patch9: 2.24-sfdisk-y-n-miscmatch.patch +# v2.24 backport: #1007885 - utmpdump is not IPv6 ready +Patch10: 2.24-utmpdump-ipv6.patch +# v2.24 backport: #1024366 - losetup does not use loop-control to add device +Patch11: 2.24-losetup-add-device.patch +# v2.24 backport: 1016471 - Document that blockdev --setbsz call has never worked +Patch12: 2.24-blockdev-setbsz-hint.patch +# v2.25 backport: #1039189 - taskset man page PERMISSIONS section is incorrect +Patch13: 2.25-taskset-man-fix-permissions.patch +# v2.25 backport: #1050852 - lscpu: support discontinuous NUMA nodes +Patch14: 2.25-lib-add-path_strdup.patch +Patch15: 2.25-lscpu-discontinuous-NUMA-nodes.patch +Patch16: 2.25-lscpu-sort-NUMA.patch +# v2.25 backport: #1046849 - filesystem type is not correctly displayed by df command +Patch17: 2.25-libblkid-Identify-extN-file-system-properly.patch +# v2.25 backport: #1055490 - libblkid: Do not problem for backup btrfs superblock +Patch18: 2.25-libblkid-no-more-probe-for-btrfs-backup-superblock.patch +# v2.25 backport: #1054186 - wipefs does not clean gpt header fully, PMBR remains +Patch19: 2.25-libblkid-detect-alone-PMBR.patch +Patch20: 2.25-wipefs-call-BLKRRPART-when-erase-partition-table.patch +# v2.25 backport: #977162 - fix the description of XFS "allocsize=" +Patch21: 2.25-mount-man-xfs.patch +# rhel7.0: #1073851 - disable user namespaces +Patch22: rhel7.0-unshare-user.patch +# v2.25 backpoprt: #1078618 - flock nfs file fails on nfsv4 +Patch23: 2.25-flock-nfs4.patch +# v2.25 backport: #1047376 - blkid hangs while reading from /dev/fd0 +Patch24: 2.25-libblkid-io-errors.patch +#v2.24 and v2.25 backport: #1079931 - fsck: warning on kdump boot +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 + +# +# RHEL 7.2 +# +Patch44: 2.25-uuidd-timeout.patch +# 1225235 - blockdev --report fails on loop and pmem devices +Patch45: 2.25-blockdev-geom.patch +# 1225053 - [RFE] backport fstrim -a and unit file +Patch46: 2.25-fstrim-all.patch +# 1131523 - mount does not appear to be correctly documented for default mount options +Patch47: 2.25-mount-man-default.patch +# 1147526 - Incorrect message printed when su is killed +Patch48: 2.26-su-coredump-message.patch +# 1165702 - make login(1) insensitive to SIGXFSZ and huge lastlog +Patch49: 2.26-login-SIGXFSZ.patch +# 1184558 - logger silently changes facility from kern to user +Patch50: 2.26-logger-man-kern.patch +# 1198243 - blkid does not recognize xfs external log device +Patch51: 2.25-libblkid-xfs-log.patch +# 1208081 - lsblk does not list dm-multipath and partitioned devices +Patch52: 2.26-lsblk-mpath.patch +# 1209594 - RHEL7: unshare: add --propagation, use MS_PRIVATE by default +# 1229436 - Remove patch that disables User Namespaces +Patch53: 2.26-unshare-rebase.patch +# 1116100 - Add -Z SELInux option to nsenter +Patch54: v2.26-nsenter-selinux.patch +# 1199619 - os-prober attempts to mount thin pool devices +Patch55: 2.25-libblkid-thinpool.patch +# 1251250 - blkid outputs nothing for some partitions (e.g. sun label) +Patch56: 2.25-libblkid-return-codes.patch +# 1182831 - blkid incorrectly detects boot sec + MBR as FAT +Patch57: 2.26-libblkid-fat.patch + +# +# RHEL7.3 +# +# 1301091 - [libblkid] Failed to get offset of the xfs_external_log signature +Patch58: 2.27-libblkid-xfs-log.patch +# 1291554 - lslogins crash when executed with buggy username +Patch59: 2.28-lslogins-rebase.patch +# 1092520 - util-linux - PIE and RELRO check +Patch60: 0060-build-sys-add-CFLAGS-and-LDFLAGS-for-daemons-and-sha.patch +# 1248003 - mount only parses <param>=<value> lines from fstab fs_spec field available from blkid block device +Patch61: 0061-libmount-be-more-restrictive-about-valid-tag-names.patch +# 1271850 - mount -a doesn't catch a typo in /etc/fstab and a typo in /etc/fstab can make a system not reboot properly +Patch62: 0062-mount-umount-swapon-fsck-lsblk-findmnt-ignore-malfor.patch +# 1290689 - util-linux: /bin/login does not retry getpwnam_r with larger buffers, leading to login failure +Patch63: 0063-login-mount-fix-__SC_GETPW_R_SIZE_MAX-usage.patch +# 1296366 - Bash completion for more(1) handles file names with spaces incorrectly +Patch64: 0064-bash-completion-use-n-as-IFS-when-ask-for-filenames.patch +# 1296521 - RHEL7: update audit event in hwclock +Patch65: 0065-hwclock-change-audit-message.patch +# 1304426 - [rfe] /bin/su should be improved to reduce stack use +Patch66: 0066-su-clean-up-groups-initialization.patch +# 1326615 - util-linux/lscpu: Fix model and model name on Power Systems +Patch67: 0067-lscpu-Fix-model-and-model-name-on-Power-Systems.patch +# 1326615 - util-linux/lscpu: Fix model and model name on Power Systems +Patch68: 0068-lscpu-use-cpu-and-revision-tag-if-available.patch +# 1335671 - extra quotes around UUID confuses findfs in RHEL (but not in Fedora) +Patch69: 0069-findfs-add-ability-to-work-with-PART-UUID-LABEL-too.patch +# 1335671 - extra quotes around UUID confuses findfs in RHEL (but not in Fedora) +Patch70: 0070-libblkid-fix-memory-leak-in-blkid_parse_tag_string.patch +# 587393 - [RFE] Make sure util-linux is ready for writable overlays +Patch71: 0071-findmnt-don-t-rely-on-st_dev-for-target.patch +# 587393 - [RFE] Make sure util-linux is ready for writable overlays +Patch72: 0072-libmount-cleanup-fs-root-detection-code.patch +# 587393 - [RFE] Make sure util-linux is ready for writable overlays +Patch73: 0073-libmount-mark-overlay-as-pseudo-FS.patch +# 1344222 - logger port option in help is misleading +Patch74: 0074-logger-be-more-precise-about-port-description.patch +# 1344482 - util-linux fails valid_pmbr() size checks if device is > 2.14TB, Device label type: dos instead of gpt +Patch75: 0075-libfdisk-gpt-be-more-careful-with-64bit-constants.patch +# 1332084 - [RFE] Inclusion of lsns command in util-linux Package +Patch76: 0076-lsns-backport-new-command.patch +# 1153770 - backport lsipc +Patch77: 0077-lib-strutils-make-strmode-more-generic.patch +# 1153770 - backport lsipc +Patch78: 0078-lsipc-backport-new-command.patch +# 1327886 - Backport blkdiscard's "-z" flag to RHEL +Patch79: 0079-blkdiscard-backport-zeroout-support.patch +# 1029385 - lack of non-ascii support +Patch80: 0080-sulogin-and-agetty-virtual-consoles-support-xvc-and-.patch +# 1298384 - RFE: add SCHED_DEADLINE support to chrt +Patch81: 0081-chrt-backport-DEADLINE-scheduler-support.patch +# 1304246 - fdisk 'f' subcommand updates partition ranges wrongly +Patch82: 0082-fdisk-backport-DOS-logical-partitions-chain-reorder.patch +# 1153770 - backport lsipc +Patch83: 0083-tests-cleanup-tests.patch +# 1281839 - [RFE]Bind mounts should be handled gracefully by the operating system +Patch84: 0084-libmount-add-support-for-bind-ro.patch +# 1007734 - blkid shows devices as /dev/block/$MAJOR:$MINOR +Patch85: 0085-libblkid-store-only-canonical-devnames-to-the-cache.patch +# 1349536 - Extended partition loop in MBR partition table leads to DOS +Patch86: 0086-libblkid-avoid-recursion-in-EBR.patch + +# +# RHEL7.4 +# +# 1405238 - findmnt --target behaviour changed in 7.3, shows all mount-points in chroot +Patch87: 0087-findmnt-fix-target-behaviour.patch +# 1419474 +Patch88: 0088-su-properly-clear-child-PID.patch +# 1344102 - fdisk does not handle 4kN devices correctly in 'Blocks' calculation +Patch89: 0089-fdisk-fix-Blocks-column-calculation.patch +# 1344720 - when fdisk detects gpt table, expected menu items seem to be missing in the 'm' output +Patch90: 0090-fdisk-fix-menu-for-GPT.patch +# 1323916 - logger unnecessarily splits messages sent via stdin into 1024 byte chunks +Patch91: 0091-logger-backport-size.patch +# 1344726 - [RFE] fdisk: output UID from gpt in fdisk when detecting that a gpt partition table is present +Patch92: 0092-fdisk-print-header-UUID-for-GPT.patch +# 1362662 - fdisk -l fails/stops on first passive/not ready device, works on RHEL6 +Patch93: 0093-fdisk-improve-l-error-handling.patch +# 1369436 - losetup man page incorrect syntax for Setup loop device +Patch94: 0094-losetup-improve-man-page-SYNOPSIS.patch +# 1392656 - [LLNL 7.4 Bug] fix buffer overflows in util-linux with upstream patch +Patch95: 0095-libblkid-fix-potential-bufer-overflows.patch +# 1370959 - [FJ7.3 Bug]: man 8 umount is wrong. +Patch96: 0096-umount-fix-obsolete-info-about-loop-in-umount.8.patch +# 1357746 - mount -av" report NFS "successfully mounted" but it is not. +Patch97: 0097-mount-fix-all-and-nofail-return-code.patch +# 1417722 - umount -a results in selinux being reported as Disabled +Patch98: 0098-umount-exclude-selinuxfs-from-all.patch +# 1402825 - RHEL7: "sfdisk -s" can't list the disk size +Patch99: 0099-sfdisk-remove-useless-CDROM-detection-for-s.patch +# 1403973 - /usr/bin/more crash on repeat search on failed regex match +Patch100: 0100-more-fix-repeat-search-crash.patch +# 1403971 - /usr/bin/more crash in end_it due to double free +Patch101: 0101-more-avoid-double-free-on-exit.patch +# 1358095 - ipcs shows wrong gid information +Patch102: 0102-ipcs-show-gid-instead-of-uid.patch +# 1358097 - ipcs shows strange status message in ja locale +Patch103: 0103-ipcs-fix-JP-status-message.patch +# 1378100 - fix swapon discard option parsing +Patch104: 0104-swapon-fix-discard-option-parsing.patch +# 1416467 - [RFE] Add support for posix_fallocate(3) call in fallocate(1) utility +Patch105: 0105-fallocate-Added-posix_fallocate-support.patch +# 1358755 - Backport request for zramctl on util-linux v2.23.2 +Patch106: 0106-zramctl-backport-from-v2.29.patch +# 1392661 - [LLNL 7.4 Bug] add zfs patches to util-linux +Patch107: 0107-libblkid-zfs-let-s-keep-compiler-happy.patch +Patch108: 0108-blkid-make-zfs-detection-more-robust.patch +Patch109: 0109-zfs-make-less-syscalls.patch +Patch110: 0110-libblkid-zfs-keep-bufferes-read-only.patch +Patch111: 0111-libblkid-don-t-mark-zfs-as-RAID.patch +Patch112: 0112-tests-update-ZFS-test.patch +Patch113: 0113-libblkid-zfs-add-cast-to-fix-UB-cppcheck.patch +Patch114: 0114-libblkid-Avoid-OOB-access-on-illegal-ZFS-superblocks.patch +# 1360764 - SMT: "Threads Per Core" will automatically change when odd or even number of vcpu are enabled online. +# 1397709 - [7.4 FEAT] Refresh code for lscpu to support s390x cpu topology +Patch115: 0115-lscpu-backport-from-v2.29.patch +# 1402183 - [HPE 7.3 Bug] fdisk -l does not list non-BTT NVDIMM devices +Patch116: 0116-fdisk-use-sysfs_devno_is_wholedisk.patch +# 1358755 - Backport request for zramctl on util-linux v2.23.2 +Patch117: 0117-zramctl-add-bash-completion.patch +Patch118: 0118-zramctl-make-mm_stat-parser-more-robust.patch +# 1344720 - when fdisk detects gpt table, expected menu items seem to be missing in the 'm' output +Patch119: 0119-fdisk-improve-menu-and-u-for-GPT.patch +# 1360764 1397709 - lscpu rebase +Patch120: 0120-tests-update-for-RHEL7.4-changes.patch +# 1358755 - Backport request for zramctl on util-linux v2.23.2 +Patch121: 0121-zramctl-be-more-specific-about-default-output.patch +# 1344726 - [RFE] fdisk: output UID from gpt in fdisk when detecting that a gpt partition table is present +Patch122: 0122-libfdisk-gpt-fix-UUID-printing.patch +# 1451704 - [Intel 7.4 BUG] IMSM RAID on 4k disks not assembled on boot +Patch123: 0123-libblkid-Add-metadata-signature-check-for-IMSM-on-4K.patch +# 1455664 - lscpu causes kernel panic on HPE Comanche / Cavium ThunderX2 (cn99xx) +Patch124: 0124-lscpu-use-sysfs-for-table-access-if-available.patch +# 1457744 - lscpu -e get crashed after offline one host cpu on amd machine +Patch125: 0125-lscpu-improve-for-offline-CPUs-on-AMD.patch + +# +# RHEL7.5 +# +# 1499760 - gvfs-udisks2-volume-monitor generates huge amount of audit log with access denied messages +Patch126: 0126-libmount-use-eacess-rather-than-open-to-check-mtab-u.patch +# 1498462 - agetty not parsing /etc/os-release correctly when processing /etc/issue +Patch127: 0127-agetty-fix-etc-os-release-parsing.patch +# 1490984 - Please update fdisk's manual page +Patch128: 0128-fdisk-remove-obsolete-info-about-GPT-from-man-page.patch +# 1487031 - [GSS][RFE]The Ceph disk type is unknown then we run the command "fdisk -l" +Patch129: 0129-libfdisk-gpt-sync-type-UUIDs-with-upstream.patch +# 1468646 - lscpu output missing Hypervisor +Patch130: 0130-lscpu-cleanup-DMI-detection-return-codes.patch +# 1455398 - flock --timeout=0 doesn't work on RHEL7 +Patch131: 0131-flock-zero-timeout-is-valid.patch +# 1336432 - journald: SYSLOG_IDENTIFIER is invalid when running logger +Patch132: 0132-logger-add-man-page-note-about-the-default-tag.patch +# 1427500 - util-linux: script does not retry on EINTR when logging command output +Patch133: 0133-script-use-all-io-to-write.patch +# 1336432 - journald: SYSLOG_IDENTIFIER is invalid when running logger +Patch134: 0134-logger-do-not-rely-only-getlogin-3-telling-who-ran-t.patch +# 1296233 - login(1) writes 0.0.0.0 IP address to wtmp when executed with "-h <ipv4-mapping-to-ipv6>" hostname +Patch135: 0135-login-use-IPv4-on-IPv4-mapping-to-IPv6.patch +# 1501953 - blkid shows wrong return code on failure with ambivalent results +Patch136: 0136-blkid-update-man-page-about-lsblk-and-ambivalent-pro.patch +# 1486777 - wipefs printing duplicate gpt clear messages +Patch137: 0137-wipefs-fix-t-filter.patch +# 1454652 - fix test suite +Patch138: 0138-tests-backport-new-ts_scsi_debug_init.patch +Patch139: 0139-tests-ts_scsi_debug_init-must-not-run-in-a-subshell.patch +Patch140: 0140-tests-cleanup-fdisk-outputs.patch +Patch141: 0141-tests-check-for-mdadm.patch +# 1496421 - [Pegas1.1 FEAT] z Systems: Move lsmem and chmem to util-linux (through rebase >= 2.30) +Patch142: 0142-lsmem-chmem-backport-new-commands.patch +Patch143: 0143-lsmem-make-split-optional-follow-output-by-default.patch +# 1499760 - gvfs-udisks2-volume-monitor generates huge amount of audit log with access denied messages +Patch144: 0144-libmount-fix-access-utab-write-test.patch +# 1520906 - make hostname in login(1) prompt optional [rhel-7.5] +Patch145: 0145-login-add-LOGIN_PLAIN_PROMPT-to-login.defs.patch +# 1521163 - [RFE] blkid can identify magic number and UUID of VDO volume +Patch146: 0146-libblkid-Add-VDO-superblock-information-into-blkid.patch +# 1534893 - RHEL7: util-linux: mount/unmount ASLR bypass via environment variable in libmount +Patch147: 0147-include-debug-don-t-print-pointer-address-for-SUID-p.patch +# 1538545 - SELinux error creating swap file +Patch148: 0148-mkswap-tolerate-ENOTSUP-when-failing-to-relabel.patch +# 1534893 - RHEL7: util-linux: mount/unmount ASLR bypass via environment variable in libmount +Patch149: 0149-libmount-fix-debug-message.patch + +# +# RHEL7.6 +# +# 1543428 - update lsns +Patch150: 0150-lsns-missing-ns-name-is-not-error.patch +Patch151: 0151-lsns-Fix-parser-for-proc-pid-stat-which-is-including.patch +# 1561350 - provide libsmartcols and libsmartcols-devel libraries +Patch152: 0152-libsmartcols-add-basic-tools-necessary-for-new-versi.patch +Patch153: 0153-libsmartcols-backport-upstream-version-v2.32-158-gc0.patch +Patch154: 0154-tests-backport-libsmartcols-tests.patch +# 1581611 - lslogins never shows lock passwords as PWD-LOCK +Patch155: 0155-lslogins-fix-password-verification.patch +# 1566674 - [RFE] Add warnings that have to be acknowledged to the -l flag in umount or remove the option +Patch156: 0156-umount-add-note-about-lazy.patch +# 1566390 - [RHEL-7.5] losetup can not detach all loop devices +Patch157: 0157-losetup-add-info-about-lazy-detach-to-manpage.patch +# 1562125 - "setarch ppc<*>" fails with "Unrecognized architecture" +Patch158: 0158-setarch-Fix-ppc64le-architectures.patch +# 1528567 - [RFE] Provide 'dig-holes' functionality for fallocate +Patch159: 0159-fallocate-backport-v2.32-164-g641af90dc.patch +# 1528959 - rbind creates duplicate entries in /proc/mounts +Patch160: 0160-libmount-fix-mnt_table_is_fs_mounted-for-rbind.patch +# 1538721 - mount man page says sync option has no effect for ext4 +Patch161: 0161-mount-add-ext4-to-some-places-in-man-page.patch +# 1252764 - icrnl is not set when autologin is used +Patch162: 0162-agetty-keep-c_iflags-unmodified-on-autologin.patch +# 1561200 - [RFE] - sulogin: improve support for locked root account +Patch163: 0163-sulogin-don-t-use-strcpy-enlarge-pwd-line-buffer.patch +Patch164: 0164-sulogin-improve-support-for-locked-root-account.patch +Patch165: 0165-sulogin-Always-make-echo-work-after-performing-getpa.patch +Patch166: 0166-sulogin-make-getpasswd-.-return-NULL-on-D.patch +Patch167: 0167-sulogin-bail-out-from-getpasswd-.-on-timeout.patch +# 1566432 - [RHEL-7.5] losetup: /dev/loop1: failed to set up loop device +Patch168: 0168-losetup-keep-f-and-devname-mutually-exclusive.patch +# 1579439 - RHEL 7.5 - lscpu shows wrong sockets, threads on max config (32TB) POWER8 machine (util-linux) +Patch169: 0169-lscpu-fix-mzx-min-MHz-reporting.patch +Patch170: 0170-chcpu-cleanup-return-codes.patch +Patch171: 0171-chcpu-cleanup-stdout-stderr-usage.patch +Patch172: 0172-lscpu-chcpu-Avoid-use-of-the-old-CPU-macros.patch +Patch173: 0173-chcpu-Fix-maximal-number-of-CPUs.patch +# 1594681 - [RHEL7.2] blkid does not output swap area +Patch174: 0174-libblkid-minix-Match-minix-superblock-types.patch +Patch175: 0175-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch +Patch176: 0176-libblkid-minix-Use-same-checks-for-version-3.patch +# 1618711 - exec option in mount unable to override the implicit noexec in user option +Patch177: 0177-mount-append-inverting-options-for-mount.-type-on-us.patch +# 1616264 - Login to emergency shell failed +Patch178: 0178-sulogin-backport-RHEL-8-version.patch + +# RHEL7.7 +# +# 1664752 - Bull (Atos) server lands up in invalid stty settings on the serial console after boot preventing login +Patch179: 0179-agetty-backport-RHEL-8-version.patch +# 1203378 - Cannot use swapon with fallocated XFS swapfile +Patch180: 0180-swapon-mkswap-sync-man-page-with-upstream.patch +# 1632944 - blkid/lsblk does not recognize exfat filesystem without a label +Patch181: 0181-libblkid-zeroize-errno-on-blkid_probe_get_buffer-suc.patch +# 1633657 - mount -a always tries to mount CIFS share subdir despite being already mounted +Patch182: 0182-libmount-fix-mount-a-for-cifs.patch +# 1639465 - Bad formatting in agetty.8 man page +Patch183: 0183-agetty-fix-man-page-formatting.patch +# 1667942 - libsmartcols: scols_print_table_to_string() doesn't work +Patch184: 0184-build-sys-add-missing-open_memstream-test.patch +# 1678451 - Unable to mount to a directory that matches a dm device under /dev +Patch185: 0185-lib-canonicalize-make-DM-canonicalization-more-robus.patch + +# RHEL-7.8 +# +# 1734545 - RHEL-7: blkid_get_dev() leak file descriptor to underlying block device +Patch186: 0186-libblkid-fix-file-descriptor-leak-in-blkid_verify.patch +# 1712768 - lsmem segfaults on ppc64 wih -o +Patch187: 0187-misc-cleanup-size_t-vs.-int-for-string_add_to_idarra.patch +# 1690102 - setarch(8) does not check error return values properly. +Patch188: 0188-setarch-use-personality-system-call-when-it-is-avail.patch +Patch189: 0189-setarch-fix-personality-syscall-return-code-check.patch +# 1734261 - lscpu not showing Physical socket, chips information +Patch190: 0190-build-sys-check-for-librtas.patch +# 1690102 - setarch(8) does not check error return values properly. +Patch191: 0191-setarch-prefer-preprocessor-rather-than-autotools-ch.patch +# 1740572 - libmount ignores empty domain field due to which plain domain option is sent. +Patch192: 0192-libmount-Preserve-empty-string-value-in-optstr-parsi.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 +others, Util-linux contains the fdisk configuration tool and the login +program. + +%package -n libsmartcols +Summary: Formatting library for ls-like programs. +Group: Development/Libraries +License: LGPLv2+ + +%description -n libsmartcols +This is library for ls-like terminal programs, part of util-linux. + + +%package -n libsmartcols-devel +Summary: Formatting library for ls-like programs. +Group: Development/Libraries +License: LGPLv2+ +Requires: libsmartcols = %{version}-%{release} +Requires: pkgconfig + +%description -n libsmartcols-devel +This is development library and headers for ls-like terminal programs, +part of util-linux. + + +%package -n libmount +Summary: Device mounting library +Group: Development/Libraries +License: LGPLv2+ +Requires: libblkid = %{version}-%{release} +Requires: libuuid = %{version}-%{release} +Conflicts: filesystem < 3 + +%description -n libmount +This is the device mounting library, part of util-linux. + + +%package -n libmount-devel +Summary: Device mounting library +Group: Development/Libraries +License: LGPLv2+ +Requires: libmount = %{version}-%{release} +Requires: pkgconfig + +%description -n libmount-devel +This is the device mounting development library and headers, +part of util-linux. + + +%package -n libblkid +Summary: Block device ID library +Group: Development/Libraries +License: LGPLv2+ +Requires: libuuid = %{version}-%{release} +Conflicts: filesystem < 3 +Requires(post): coreutils + +%description -n libblkid +This is block device identification library, part of util-linux. + + +%package -n libblkid-devel +Summary: Block device ID library +Group: Development/Libraries +License: LGPLv2+ +Requires: libblkid = %{version}-%{release} +Requires: pkgconfig + +%description -n libblkid-devel +This is the block device identification development library and headers, +part of util-linux. + + +%package -n libuuid +Summary: Universally unique ID library +Group: Development/Libraries +License: BSD +Conflicts: filesystem < 3 + +%description -n libuuid +This is the universally unique ID library, part of util-linux. + +The libuuid library generates and parses 128-bit universally unique +id's (UUID's). A UUID is an identifier that is unique across both +space and time, with respect to the space of all UUIDs. A UUID can +be used for multiple purposes, from tagging objects with an extremely +short lifetime, to reliably identifying very persistent objects +across a network. + +See also the "uuid" package, which is a separate implementation. + +%package -n libuuid-devel +Summary: Universally unique ID library +Group: Development/Libraries +License: BSD +Requires: libuuid = %{version}-%{release} +Requires: pkgconfig + +%description -n libuuid-devel +This is the universally unique ID development library and headers, +part of util-linux. + +The libuuid library generates and parses 128-bit universally unique +id's (UUID's). A UUID is an identifier that is unique across both +space and time, with respect to the space of all UUIDs. A UUID can +be used for multiple purposes, from tagging objects with an extremely +short lifetime, to reliably identifying very persistent objects +across a network. + +See also the "uuid-devel" package, which is a separate implementation. + + +%package -n uuidd +Summary: Helper daemon to guarantee uniqueness of time-based UUIDs +Group: System Environment/Daemons +Requires: libuuid = %{version}-%{release} +License: GPLv2 +Requires: systemd +Requires(pre): shadow-utils +Requires(post): systemd-units +Requires(preun): systemd-units + +%description -n uuidd +The uuidd package contains a userspace daemon (uuidd) which guarantees +uniqueness of time-based UUID generation even at very high rates on +SMP systems. + + +%prep +%autosetup -p1 -n %{name}-%{upstream_version} + +cp %{SOURCE8} %{SOURCE9} . + +%build +unset LINGUAS || : + +# unfortunately, we did changes to build-system +./autogen.sh + +# we modify .po files by RHEL patches +rm -f po/stamp* + +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" +export DAEMON_CFLAGS="$SUID_CFLAGS" +export DAEMON_LDFLAGS="$SUID_LDFLAGS" +%configure \ + --with-systemdsystemunitdir=%{_unitdir} \ + --disable-silent-rules \ + --disable-wall \ + --disable-bfs \ + --disable-pg \ + --enable-socket-activation \ + --enable-chfn-chsh \ + --enable-write \ + --enable-raw \ + --with-udev \ + --with-selinux \ + --with-audit \ + --with-utempter \ + --disable-makeinstall-chown \ +%ifnarch %cytune_archs + --disable-cytune \ +%endif +%ifarch s390 s390x + --disable-hwclock \ + --disable-fdformat +%endif + +# build util-linux +make %{?_smp_mflags} + +# build nologin +gcc $CFLAGS -o nologin nologin.c + + +%check +#to run tests use "--with check" +%if %{?_with_check:1}%{!?_with_check:0} +make check +%endif + + +%install +rm -rf ${RPM_BUILD_ROOT} +mkdir -p ${RPM_BUILD_ROOT}%{_bindir} +mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man{1,6,8,5} +mkdir -p ${RPM_BUILD_ROOT}%{_sbindir} +mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/{pam.d,security/console.apps} +mkdir -p ${RPM_BUILD_ROOT}/var/log +touch ${RPM_BUILD_ROOT}/var/log/lastlog +chmod 0644 ${RPM_BUILD_ROOT}/var/log/lastlog + +# install util-linux +make install DESTDIR=${RPM_BUILD_ROOT} + +# install nologin +install -m 755 nologin ${RPM_BUILD_ROOT}%{_sbindir} +install -m 644 nologin.8 ${RPM_BUILD_ROOT}%{_mandir}/man8 + +# raw +echo '.so man8/raw.8' > $RPM_BUILD_ROOT%{_mandir}/man8/rawdevices.8 +{ + # see RH bugzilla #216664 + mkdir -p ${RPM_BUILD_ROOT}%{_prefix}/lib/udev/rules.d + pushd ${RPM_BUILD_ROOT}%{_prefix}/lib/udev/rules.d + install -m 644 %{SOURCE4} ./60-raw.rules + popd +} + +# sbin -> bin +mv ${RPM_BUILD_ROOT}%{_sbindir}/raw ${RPM_BUILD_ROOT}%{_bindir}/raw + +# And a dirs uuidd needs that the makefiles don't create +install -d ${RPM_BUILD_ROOT}/run/uuidd +install -d ${RPM_BUILD_ROOT}/var/lib/libuuid + +# libtool junk +rm -rf ${RPM_BUILD_ROOT}%{_libdir}/*.la + +%ifarch %{sparc} +rm -rf ${RPM_BUILD_ROOT}%{_bindir}/sunhostid +cat << E-O-F > ${RPM_BUILD_ROOT}%{_bindir}/sunhostid +#!/bin/sh +# this should be _bindir/sunhostid or somesuch. +# Copyright 1999 Peter Jones, <pjones@redhat.com> . +# GPL and all that good stuff apply. +( +idprom=\`cat /proc/openprom/idprom\` +echo \$idprom|dd bs=1 skip=2 count=2 +echo \$idprom|dd bs=1 skip=27 count=6 +echo +) 2>/dev/null +E-O-F +chmod 755 ${RPM_BUILD_ROOT}%{_bindir}/sunhostid +%endif + +# PAM settings +{ + pushd ${RPM_BUILD_ROOT}%{_sysconfdir}/pam.d + install -m 644 %{SOURCE1} ./login + install -m 644 %{SOURCE2} ./remote + install -m 644 %{SOURCE3} ./chsh + install -m 644 %{SOURCE3} ./chfn + install -m 644 %{SOURCE12} ./su + install -m 644 %{SOURCE13} ./su-l + install -m 644 %{SOURCE14} ./runuser + install -m 644 %{SOURCE15} ./runuser-l + popd +} + +%ifnarch s390 s390x +ln -sf hwclock ${RPM_BUILD_ROOT}%{_sbindir}/clock +echo ".so man8/hwclock.8" > ${RPM_BUILD_ROOT}%{_mandir}/man8/clock.8 +%endif + +# unsupported on SPARCs +%ifarch %{sparc} +for I in /sbin/sfdisk \ + %{_mandir}/man8/sfdisk.8* \ + %doc Documentation/sfdisk.txt \ + /sbin/cfdisk \ + %{_mandir}/man8/cfdisk.8*; do + + rm -f $RPM_BUILD_ROOT$I +done +%endif + +# we install getopt-*.{bash,tcsh} as doc files +chmod 644 misc-utils/getopt-*.{bash,tcsh} +rm -f ${RPM_BUILD_ROOT}%{_datadir}/getopt/* +rmdir ${RPM_BUILD_ROOT}%{_datadir}/getopt + +ln -sf ../proc/self/mounts %{buildroot}/etc/mtab + +# remove static libs +rm -f $RPM_BUILD_ROOT%{_libdir}/lib{uuid,blkid,mount,smartcols}.a + +# find MO files +%find_lang %name + +# the files section supports only one -f option... +mv %{name}.lang %{name}.files + +# create list of setarch(8) symlinks +find $RPM_BUILD_ROOT%{_bindir}/ -regextype posix-egrep -type l \ + -regex ".*(linux32|linux64|s390|s390x|i386|ppc|ppc64|ppc32|sparc|sparc64|sparc32|sparc32bash|mips|mips64|mips32|ia64|x86_64)$" \ + -printf "%{_bindir}/%f\n" >> %{name}.files + +find $RPM_BUILD_ROOT%{_mandir}/man8 -regextype posix-egrep \ + -regex ".*(linux32|linux64|s390|s390x|i386|ppc|ppc64|ppc32|sparc|sparc64|sparc32|sparc32bash|mips|mips64|mips32|ia64|x86_64)\.8.*" \ + -printf "%{_mandir}/man8/%f*\n" >> %{name}.files + +%post +# only for minimal buildroots without /var/log +[ -d /var/log ] || mkdir -p /var/log +touch /var/log/lastlog +chown root:root /var/log/lastlog +chmod 0644 /var/log/lastlog +# Fix the file context, do not use restorecon +if [ -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then + SECXT=$( /usr/sbin/matchpathcon -n /var/log/lastlog 2> /dev/null ) + if [ -n "$SECXT" ]; then + # Selinux enabled, but without policy? It's true for buildroots + # without selinux stuff on host machine with enabled selinux. + # We don't want to use any RPM dependence on selinux policy for + # matchpathcon(2). SELinux policy should be optional. + /usr/bin/chcon "$SECXT" /var/log/lastlog >/dev/null 2>&1 || : + fi +fi +if [ ! -L /etc/mtab ]; then + ln -sf ../proc/self/mounts /etc/mtab || : +fi + +%post -n libblkid +/sbin/ldconfig + +### Move blkid cache to /run +[ -d /run/blkid ] || mkdir -p /run/blkid +for I in /etc/blkid.tab /etc/blkid.tab.old \ + /etc/blkid/blkid.tab /etc/blkid/blkid.tab.old; do + + if [ -f "$I" ]; then + mv "$I" /run/blkid/ || : + fi +done + +%postun -n libblkid -p /sbin/ldconfig + +%post -n libuuid -p /sbin/ldconfig +%postun -n libuuid -p /sbin/ldconfig + +%post -n libmount -p /sbin/ldconfig +%postun -n libmount -p /sbin/ldconfig + +%post -n libsmartcols -p /sbin/ldconfig +%postun -n libsmartcols -p /sbin/ldconfig + +%pre -n uuidd +getent group uuidd >/dev/null || groupadd -r uuidd +getent passwd uuidd >/dev/null || \ +useradd -r -g uuidd -d /var/lib/libuuid -s /sbin/nologin \ + -c "UUID generator helper daemon" uuidd +exit 0 + +%post -n uuidd +if [ $1 -eq 1 ]; then + # Package install, + /bin/systemctl enable uuidd.service >/dev/null 2>&1 || : + /bin/systemctl start uuidd.service > /dev/null 2>&1 || : +else + # Package upgrade + if /bin/systemctl --quiet is-enabled uuidd.service ; then + /bin/systemctl reenable uuidd.service >/dev/null 2>&1 || : + fi +fi + +%preun -n uuidd +if [ "$1" = 0 ]; then + /bin/systemctl stop uuidd.service > /dev/null 2>&1 || : + /bin/systemctl disable uuidd.service > /dev/null 2>&1 || : +fi + + +%files -f %{name}.files +%defattr(-,root,root) +%doc README NEWS AUTHORS +%doc Documentation/deprecated.txt Documentation/licenses/* +%doc misc-utils/getopt-*.{bash,tcsh} + +%config(noreplace) %{_sysconfdir}/pam.d/chfn +%config(noreplace) %{_sysconfdir}/pam.d/chsh +%config(noreplace) %{_sysconfdir}/pam.d/login +%config(noreplace) %{_sysconfdir}/pam.d/remote +%config(noreplace) %{_sysconfdir}/pam.d/su +%config(noreplace) %{_sysconfdir}/pam.d/su-l +%config(noreplace) %{_sysconfdir}/pam.d/runuser +%config(noreplace) %{_sysconfdir}/pam.d/runuser-l +%config(noreplace) %{_prefix}/lib/udev/rules.d/60-raw.rules + +%attr(4755,root,root) %{_bindir}/mount +%attr(4755,root,root) %{_bindir}/umount +%attr(4755,root,root) %{_bindir}/su +%attr(755,root,root) %{_bindir}/login +%attr(4711,root,root) %{_bindir}/chfn +%attr(4711,root,root) %{_bindir}/chsh +%attr(2755,root,tty) %{_bindir}/write + +%ghost %attr(0644,root,root) %verify(not md5 size mtime) /var/log/lastlog +%ghost %verify(not md5 size mtime) %config(noreplace,missingok) /etc/mtab + +%{_unitdir}/fstrim.* + +%{_bindir}/cal +%{_bindir}/chrt +%{_bindir}/col +%{_bindir}/colcrt +%{_bindir}/colrm +%{_bindir}/column +%{_bindir}/chmem +%{_bindir}/dmesg +%{_bindir}/eject +%{_bindir}/fallocate +%{_bindir}/findmnt +%{_bindir}/flock +%{_bindir}/getopt +%{_bindir}/hexdump +%{_bindir}/ionice +%{_bindir}/ipcmk +%{_bindir}/ipcrm +%{_bindir}/ipcs +%{_bindir}/isosize +%{_bindir}/kill +%{_bindir}/lsipc +%{_bindir}/lsns +%{_bindir}/lsmem +%{_bindir}/logger +%{_bindir}/look +%{_bindir}/lsblk +%{_bindir}/lscpu +%{_bindir}/lslocks +%{_bindir}/lslogins +%{_bindir}/mcookie +%{_bindir}/more +%{_bindir}/mountpoint +%{_bindir}/namei +%{_bindir}/nsenter +%{_bindir}/prlimit +%{_bindir}/raw +%{_bindir}/rename +%{_bindir}/renice +%{_bindir}/rev +%{_bindir}/script +%{_bindir}/scriptreplay +%{_bindir}/setarch +%{_bindir}/setpriv +%{_bindir}/setsid +%{_bindir}/setterm +%{_bindir}/tailf +%{_bindir}/taskset +%{_bindir}/ul +%{_bindir}/unshare +%{_bindir}/utmpdump +%{_bindir}/uuidgen +%{_bindir}/wdctl +%{_bindir}/whereis +%{_mandir}/man1/cal.1* +%{_mandir}/man1/chfn.1* +%{_mandir}/man1/chrt.1* +%{_mandir}/man1/chsh.1* +%{_mandir}/man1/col.1* +%{_mandir}/man1/colcrt.1* +%{_mandir}/man1/colrm.1* +%{_mandir}/man1/column.1* +%{_mandir}/man1/dmesg.1* +%{_mandir}/man1/eject.1* +%{_mandir}/man1/fallocate.1* +%{_mandir}/man1/flock.1* +%{_mandir}/man1/getopt.1* +%{_mandir}/man1/hexdump.1* +%{_mandir}/man1/ionice.1* +%{_mandir}/man1/ipcmk.1* +%{_mandir}/man1/ipcrm.1* +%{_mandir}/man1/ipcs.1* +%{_mandir}/man1/kill.1* +%{_mandir}/man1/logger.1* +%{_mandir}/man1/login.1* +%{_mandir}/man1/look.1* +%{_mandir}/man1/lscpu.1* +%{_mandir}/man1/lslogins.1* +%{_mandir}/man1/lsmem.1* +%{_mandir}/man1/lsipc.1* +%{_mandir}/man1/mcookie.1* +%{_mandir}/man1/more.1* +%{_mandir}/man1/mountpoint.1* +%{_mandir}/man1/namei.1* +%{_mandir}/man1/nsenter.1* +%{_mandir}/man1/prlimit.1* +%{_mandir}/man1/rename.1* +%{_mandir}/man1/renice.1* +%{_mandir}/man1/rev.1* +%{_mandir}/man1/runuser.1* +%{_mandir}/man1/script.1* +%{_mandir}/man1/scriptreplay.1* +%{_mandir}/man1/setpriv.1* +%{_mandir}/man1/setsid.1* +%{_mandir}/man1/setterm.1* +%{_mandir}/man1/su.1* +%{_mandir}/man1/tailf.1* +%{_mandir}/man1/taskset.1* +%{_mandir}/man1/ul.1* +%{_mandir}/man1/unshare.1* +%{_mandir}/man1/utmpdump.1.gz +%{_mandir}/man1/uuidgen.1* +%{_mandir}/man1/whereis.1* +%{_mandir}/man1/write.1* +%{_mandir}/man5/fstab.5* +%{_mandir}/man8/addpart.8* +%{_mandir}/man8/agetty.8* +%{_mandir}/man8/blkdiscard.8* +%{_mandir}/man8/blkid.8* +%{_mandir}/man8/blockdev.8* +%{_mandir}/man8/chcpu.8* +%{_mandir}/man8/chmem.8* +%{_mandir}/man8/ctrlaltdel.8* +%{_mandir}/man8/delpart.8* +%{_mandir}/man8/fdisk.8* +%{_mandir}/man8/findfs.8* +%{_mandir}/man8/findmnt.8* +%{_mandir}/man8/fsck.8* +%{_mandir}/man8/fsck.cramfs.8* +%{_mandir}/man8/fsck.minix.8* +%{_mandir}/man8/fsfreeze.8* +%{_mandir}/man8/fstrim.8* +%{_mandir}/man8/isosize.8* +%{_mandir}/man8/ldattach.8* +%{_mandir}/man8/losetup.8* +%{_mandir}/man8/lsblk.8* +%{_mandir}/man8/lslocks.8* +%{_mandir}/man8/lsns.8* +%{_mandir}/man8/mkfs.8* +%{_mandir}/man8/mkfs.cramfs.8* +%{_mandir}/man8/mkfs.minix.8* +%{_mandir}/man8/mkswap.8* +%{_mandir}/man8/mount.8* +%{_mandir}/man8/nologin.8* +%{_mandir}/man8/partx.8* +%{_mandir}/man8/pivot_root.8* +%{_mandir}/man8/raw.8* +%{_mandir}/man8/rawdevices.8* +%{_mandir}/man8/readprofile.8* +%{_mandir}/man8/resizepart.8* +%{_mandir}/man8/rtcwake.8* +%{_mandir}/man8/setarch.8* +%{_mandir}/man8/sulogin.8* +%{_mandir}/man8/swaplabel.8* +%{_mandir}/man8/swapoff.8* +%{_mandir}/man8/swapon.8* +%{_mandir}/man8/switch_root.8* +%{_mandir}/man8/umount.8* +%{_mandir}/man8/wdctl.8* +%{_mandir}/man8/wipefs.8* +%{_mandir}/man8/zramctl.8* +%{_sbindir}/addpart +%{_sbindir}/agetty +%{_sbindir}/blkdiscard +%{_sbindir}/blkid +%{_sbindir}/blockdev +%{_sbindir}/chcpu +%{_sbindir}/ctrlaltdel +%{_sbindir}/delpart +%{_sbindir}/fdisk +%{_sbindir}/findfs +%{_sbindir}/fsck +%{_sbindir}/fsck.cramfs +%{_sbindir}/fsck.minix +%{_sbindir}/fsfreeze +%{_sbindir}/fstrim +%{_sbindir}/ldattach +%{_sbindir}/losetup +%{_sbindir}/mkfs +%{_sbindir}/mkfs.cramfs +%{_sbindir}/mkfs.minix +%{_sbindir}/mkswap +%{_sbindir}/nologin +%{_sbindir}/partx +%{_sbindir}/pivot_root +%{_sbindir}/readprofile +%{_sbindir}/resizepart +%{_sbindir}/rtcwake +%{_sbindir}/runuser +%{_sbindir}/sulogin +%{_sbindir}/swaplabel +%{_sbindir}/swapoff +%{_sbindir}/swapon +%{_sbindir}/switch_root +%{_sbindir}/wipefs +%{_sbindir}/zramctl +%{compldir}/addpart +%{compldir}/blkdiscard +%{compldir}/blkid +%{compldir}/blockdev +%{compldir}/cal +%{compldir}/chcpu +%{compldir}/chfn +%{compldir}/chrt +%{compldir}/chsh +%{compldir}/col +%{compldir}/colcrt +%{compldir}/colrm +%{compldir}/column +%{compldir}/ctrlaltdel +%{compldir}/delpart +%{compldir}/dmesg +%{compldir}/eject +%{compldir}/fallocate +%{compldir}/fdisk +%{compldir}/findmnt +%{compldir}/flock +%{compldir}/fsck +%{compldir}/fsck.cramfs +%{compldir}/fsck.minix +%{compldir}/fsfreeze +%{compldir}/fstrim +%{compldir}/getopt +%{compldir}/hexdump +%{compldir}/ionice +%{compldir}/ipcrm +%{compldir}/ipcs +%{compldir}/isosize +%{compldir}/ldattach +%{compldir}/logger +%{compldir}/look +%{compldir}/losetup +%{compldir}/lsblk +%{compldir}/lscpu +%{compldir}/lslocks +%{compldir}/mcookie +%{compldir}/mkfs +%{compldir}/mkfs.cramfs +%{compldir}/mkfs.minix +%{compldir}/mkswap +%{compldir}/more +%{compldir}/mountpoint +%{compldir}/namei +%{compldir}/nsenter +%{compldir}/partx +%{compldir}/pivot_root +%{compldir}/prlimit +%{compldir}/raw +%{compldir}/readprofile +%{compldir}/rename +%{compldir}/renice +%{compldir}/resizepart +%{compldir}/rev +%{compldir}/rtcwake +%{compldir}/runuser +%{compldir}/script +%{compldir}/scriptreplay +%{compldir}/setarch +%{compldir}/setpriv +%{compldir}/setsid +%{compldir}/setterm +%{compldir}/su +%{compldir}/swaplabel +%{compldir}/swapon +%{compldir}/tailf +%{compldir}/taskset +%{compldir}/ul +%{compldir}/unshare +%{compldir}/utmpdump +%{compldir}/uuidgen +%{compldir}/wdctl +%{compldir}/whereis +%{compldir}/wipefs +%{compldir}/write +%{compldir}/zramctl + +%ifnarch s390 s390x +%{_sbindir}/clock +%{_sbindir}/fdformat +%{_sbindir}/hwclock +%{_mandir}/man8/fdformat.8* +%{_mandir}/man8/hwclock.8* +%{_mandir}/man8/clock.8* +%{compldir}/fdformat +%{compldir}/hwclock +%endif + +%ifnarch %{sparc} +%doc Documentation/sfdisk.txt +%{_sbindir}/cfdisk +%{_sbindir}/sfdisk +%{_mandir}/man8/cfdisk.8* +%{_mandir}/man8/sfdisk.8* +%{compldir}/cfdisk +%{compldir}/sfdisk +%endif + +%ifarch %{sparc} +%{_bindir}/sunhostid +%endif + +%ifarch %cytune_archs +%{_bindir}/cytune +%{_mandir}/man8/cytune.8* +%{compldir}/cytune +%endif + + +%files -n uuidd +%defattr(-,root,root) +%doc Documentation/licenses/COPYING.GPLv2 +%{_mandir}/man8/uuidd.8* +%{_sbindir}/uuidd +%{_unitdir}/uuidd.* +%dir %attr(2775, uuidd, uuidd) /var/lib/libuuid +%dir %attr(2775, uuidd, uuidd) /run/uuidd +%{compldir}/uuidd + + +%files -n libsmartcols +%defattr(-,root,root) +%doc libsmartcols/COPYING +%{_libdir}/libsmartcols.so.* + +%files -n libsmartcols-devel +%defattr(-,root,root) +%{_libdir}/libsmartcols.so +%{_includedir}/libsmartcols +%{_libdir}/pkgconfig/smartcols.pc + + +%files -n libmount +%defattr(-,root,root) +%doc libmount/COPYING +%{_libdir}/libmount.so.* + +%files -n libmount-devel +%defattr(-,root,root) +%doc libmount/COPYING +%{_libdir}/libmount.so +%{_includedir}/libmount +%{_libdir}/pkgconfig/mount.pc + + +%files -n libblkid +%defattr(-,root,root) +%doc libblkid/COPYING +%{_libdir}/libblkid.so.* + +%files -n libblkid-devel +%defattr(-,root,root) +%doc libblkid/COPYING +%{_libdir}/libblkid.so +%{_includedir}/blkid +%{_mandir}/man3/libblkid.3* +%{_libdir}/pkgconfig/blkid.pc + + +%files -n libuuid +%defattr(-,root,root) +%doc libuuid/COPYING +%{_libdir}/libuuid.so.* + +%files -n libuuid-devel +%defattr(-,root,root) +%doc libuuid/COPYING +%{_libdir}/libuuid.so +%{_includedir}/uuid +%{_mandir}/man3/uuid.3* +%{_mandir}/man3/uuid_clear.3* +%{_mandir}/man3/uuid_compare.3* +%{_mandir}/man3/uuid_copy.3* +%{_mandir}/man3/uuid_generate.3* +%{_mandir}/man3/uuid_generate_random.3* +%{_mandir}/man3/uuid_generate_time.3* +%{_mandir}/man3/uuid_generate_time_safe.3* +%{_mandir}/man3/uuid_is_null.3* +%{_mandir}/man3/uuid_parse.3* +%{_mandir}/man3/uuid_time.3* +%{_mandir}/man3/uuid_unparse.3* +%{_libdir}/pkgconfig/uuid.pc + +%changelog +* Wed Aug 21 2019 Karel Zak <kzak@redhat.com> 2.23.2-63 +- fix #1740572 - libmount ignores empty domain field due to which plain domain option is sent. + +* Tue Aug 06 2019 Karel Zak <kzak@redhat.com> 2.23.2-62 +- fix #1734545 - blkid_get_dev() leak file descriptor to underlying block device +- fix #1712768 - lsmem segfaults on ppc64 wih -o +- fix #1690102 - setarch(8) does not check error return values properly. +- fix #1734261 - lscpu not showing Physical socket, chips information + +* Fri Mar 22 2019 Karel Zak <kzak@redhat.com> 2.23.2-61 +- #1203378 - Cannot use swapon with fallocated XFS swapfile +- #1632944 - blkid/lsblk does not recognize exfat filesystem without a label +- #1633657 - mount -a always tries to mount CIFS share subdir despite being already mounted +- #1639465 - Bad formatting in agetty.8 man page +- #1667942 - libsmartcols: scols_print_table_to_string() doesn't work +- #1678451 - Unable to mount to a directory that matches a dm device under /dev + +* Thu Jan 31 2019 Karel Zak <kzak@redhat.com> 2.23.2-60 +- fix #1664752 - Bull (Atos) server lands up in invalid stty settings on the serial console after boot preventing login + +* Mon Aug 20 2018 Karel Zak <kzak@redhat.com> 2.23.2-59 +- fix #1618711 - exec option in mount unable to override the implicit noexec in user option +- fix #1616264 - Login to emergency shell failed + +* Wed Jul 04 2018 Karel Zak <kzak@redhat.com> 2.23.2-58 +- fix #1594681 - [RHEL7.2] blkid does not output swap area + +* Thu Jun 14 2018 Karel Zak <kzak@redhat.com> 2.23.2-57 +- add another ppc aliases to setarch (#1562125) + +* Wed Jun 13 2018 Karel Zak <kzak@redhat.com> 2.23.2-56 +- backport another chcpu patches for #1579439 + +* Fri Jun 08 2018 Karel Zak <kzak@redhat.com> 2.23.2-55 +- improve fallocate patch backward compatibility (#1528567) + +* Thu Jun 07 2018 Karel Zak <kzak@redhat.com> 2.23.2-54 +- fix #1581611 - lslogins never shows lock passwords as PWD-LOCK +- fix #1579439 - RHEL 7.5 - lscpu shows wrong sockets, threads on max config (32TB) POWER8 machine (util-linux) +- fix #1566674 - [RFE] Add warnings that have to be acknowledged to the -l flag in umount or remove the option +- fix #1566390 - [RHEL-7.5] losetup can not detach all loop devices +- fix #1562125 - "setarch ppc<*>" fails with "Unrecognized architecture" +- fix #1528567 - [RFE] Provide 'dig-holes' functionality for fallocate +- fix #1528959 - rbind creates duplicate entries in /proc/mounts +- fix #1538721 - mount man page says sync option has no effect for ext4 +- fix #1252764 - icrnl is not set when autologin is used +- fix #1561200 - [RFE] - sulogin: improve support for locked root account +- fix #1566432 - [RHEL-7.5] losetup: /dev/loop1: failed to set up loop device + +* Thu May 31 2018 Karel Zak <kzak@redhat.com> 2.23.2-53 +- fix #1543428 - update lsns +- fix #1561350 - provide libsmartcols and libsmartcols-devel libraries + +* Fri Feb 02 2018 Karel Zak <kzak@redhat.com> 2.23.2-52 +- fix #1534893 - RHEL7: util-linux: mount/unmount ASLR bypass via environment variable in libmount + +* Wed Jan 31 2018 Karel Zak <kzak@redhat.com> 2.23.2-51 +- fix #1538545 - SELinux error creating swap file + +* Tue Jan 23 2018 Karel Zak <kzak@redhat.com> 2.23.2-50 +- fix #1534893 - RHEL7: util-linux: mount/unmount ASLR bypass via environment variable in libmount + +* Mon Dec 11 2017 Karel Zak <kzak@redhat.com> 2.23.2-49 +- fix #1520906 - make hostname in login(1) prompt optional +- fix #1521163 - [RFE] blkid can identify magic number and UUID of VDO volume + +* Thu Nov 09 2017 Karel Zak <kzak@redhat.com> 2.23.2-48 +- improve libmount eaccess test (#1499760) + +* Wed Nov 08 2017 Karel Zak <kzak@redhat.com> 2.23.2-47 +- improve lsmem (#1496421) + +* Mon Oct 23 2017 Karel Zak <kzak@redhat.com> 2.23.2-46 +- fix #1496421 - Move lsmem and chmem to util-linux + +* Tue Oct 17 2017 Karel Zak <kzak@redhat.com> 2.23.2-45 +- fix #1501953 - blkid shows wrong return code on failure with ambivalent results +- fix #1486777 - wipefs printing duplicate gpt clear messages +- fix #1454652 - fix test suite + +* Mon Oct 16 2017 Karel Zak <kzak@redhat.com> 2.23.2-44 +- fix #1478002 - systemd read-only container produces errors +- fix #1499760 - gvfs-udisks2-volume-monitor generates huge amount of audit log with access denied messages +- fix #1498462 - agetty not parsing /etc/os-release correctly when processing /etc/issue +- fix #1490984 - Please update fdisk's manual page +- fix #1487031 - [GSS][RFE]The Ceph disk type is unknown then we run the command "fdisk -l" +- fix #1468646 - lscpu output missing Hypervisor +- fix #1455398 - flock --timeout=0 doesn't work on RHEL7 +- fix #1336432 - journald: SYSLOG_IDENTIFIER is invalid when running logger +- fix #1427500 - util-linux: script does not retry on EINTR when logging command output +- fix #1336432 - journald: SYSLOG_IDENTIFIER is invalid when running logger +- fix #1296233 - login(1) writes 0.0.0.0 IP address to wtmp when executed with "-h <ipv4-mapping-to-ipv6>" hostname + +* Thu Jun 01 2017 Karel Zak <kzak@redhat.com> 2.23.2-43 +- fix #1457744 - lscpu -e get crashed after offline one host cpu on amd machine + +* Wed May 31 2017 Karel Zak <kzak@redhat.com> 2.23.2-42 +- fix #1455664 - lscpu causes kernel panic on HPE Comanche / Cavium ThunderX2 (cn99xx) + +* Tue May 23 2017 Karel Zak <kzak@redhat.com> 2.23.2-41 +- fix #1451704 - IMSM RAID on 4k disks not assembled on boot +- force gettext messages refresh (for #1358097 bugfix) + +* Wed May 10 2017 Karel Zak <kzak@redhat.com> 2.23.2-40 +- improve libfdisk GPT UUID printing (#1344726) + +* Tue Apr 04 2017 Karel Zak <kzak@redhat.com> 2.23.2-39 +- update package regression tests (due to #1360764 #1397709) +- improve zramctl man page (#1358755) + +* Tue Apr 04 2017 Karel Zak <kzak@redhat.com> 2.23.2-38 +- improve fdisk menu for GPT (#1344720) + +* Thu Mar 30 2017 Karel Zak <kzak@redhat.com> 2.23.2-37 +- improve zram mm_stat parser (#1358755) + +* Wed Mar 22 2017 Karel Zak <kzak@redhat.com> 2.23.2-36 +- fix #1344102 - fdisk does not handle 4kN devices correctly in 'Blocks' calculation +- fix #1344720 - when fdisk detects gpt table, expected menu items seem to be missing in the 'm' output +- fix #1323916 - logger unnecessarily splits messages sent via stdin into 1024 byte chunks +- fix #1344726 - [RFE] fdisk: output UID from gpt in fdisk when detecting that a gpt partition table is present +- fix #1362662 - fdisk -l fails/stops on first passive/not ready device, works on RHEL6 +- fix #1369436 - losetup man page incorrect syntax for Setup loop device +- fix #1392656 - [LLNL 7.4 Bug] fix buffer overflows in util-linux with upstream patch +- fix #1370959 - [FJ7.3 Bug]: man 8 umount is wrong. +- fix #1357746 - mount -av" report NFS "successfully mounted" but it is not. +- fix #1417722 - umount -a results in selinux being reported as Disabled +- fix #1402825 - RHEL7: "sfdisk -s" can't list the disk size +- fix #1403973 - /usr/bin/more crash on repeat search on failed regex match +- fix #1403971 - /usr/bin/more crash in end_it due to double free +- fix #1358095 - ipcs shows wrong gid information +- fix #1358097 - ipcs shows strange status message in ja locale +- fix #1378100 - fix swapon discard option parsing +- fix #1416467 - [RFE] Add support for posix_fallocate(3) call in fallocate(1) utility +- fix #1358755 - Backport request for zramctl on util-linux v2.23.2 +- fix #1392661 - [LLNL 7.4 Bug] add zfs patches to util-linux +- fix #1360764 - SMT: "Threads Per Core" will automatically change when odd or even number of vcpu are enabled online. +- fix #1397709 - [7.4 FEAT] Refresh code for lscpu to support s390x cpu topology +- fix #1402183 - [HPE 7.3 Bug] fdisk -l does not list non-BTT NVDIMM devices + +* Wed Feb 8 2017 Karel Zak <kzak@redhat.com> 2.23.2-35 +* fix #1419474 - su: properly clear child PID + +* Mon Jan 23 2017 Karel Zak <kzak@redhat.com> 2.23.2-34 +- fix #1405238 - findmnt --target behaviour changed in 7.3, shows all mount-points in chroot + +* Tue Jul 12 2016 Karel Zak <kzak@redhat.com> 2.23.2-33 +- improve patch for #1007734 (libblkid realpaths) + +* Tue Jul 12 2016 Karel Zak <kzak@redhat.com> 2.23.2-32 +- improve patch for chrt(1) deadline support #1298384 +- fix #1007734 - blkid shows devices as /dev/block/$MAJOR:$MINOR +- fix #1349536 - Extended partition loop in MBR partition table leads to DOS + +* Mon Jul 04 2016 Karel Zak <kzak@redhat.com> 2.23.2-31 +- improve spec file for #1092520 + +* Fri Jul 01 2016 Karel Zak <kzak@redhat.com> 2.23.2-30 +- improve patch for chrt(1) deadline support #1298384 +- improve regression tests + +* Thu Jun 30 2016 Karel Zak <kzak@redhat.com> 2.23.2-29 +- fix #1029385 - lack of non-ascii support +- fix #1092520 - util-linux - PIE and RELRO check +- fix #1153770 - backport lsipc +- fix #1248003 - mount only parses <param>=<value> lines from fstab fs_spec field available from blkid block device +- fix #1271850 - mount -a doesn't catch a typo in /etc/fstab and a typo in /etc/fstab can make a system not reboot properly +- fix #1281839 - [RFE]Bind mounts should be handled gracefully by the operating system +- fix #1290689 - util-linux: /bin/login does not retry getpwnam_r with larger buffers, leading to login failure +- fix #1296366 - Bash completion for more(1) handles file names with spaces incorrectly +- fix #1296521 - RHEL7: update audit event in hwclock +- fix #1298384 - RFE: add SCHED_DEADLINE support to chrt +- fix #1304246 - fdisk 'f' subcommand updates partition ranges wrongly +- fix #1304426 - [rfe] /bin/su should be improved to reduce stack use +- fix #1326615 - util-linux/lscpu: Fix model and model name on Power Systems +- fix #1327886 - Backport blkdiscard's "-z" flag to RHEL +- fix #1332084 - [RFE] Inclusion of lsns command in util-linux Package +- fix #1335671 - extra quotes around UUID confuses findfs in RHEL (but not in Fedora) +- fix #1344222 - logger port option in help is misleading +- fix #1344482 - util-linux fails valid_pmbr() size checks if device is > 2.14TB, Device label type: dos instead of gpt +- fix #587393 - [RFE] Make sure util-linux is ready for writable overlays + +* Wed Mar 16 2016 Karel Zak <kzak@redhat.com> 2.23.2-28 +- fix #1291554 - lslogins crash when executed with buggy username + +* Thu Jan 28 2016 Karel Zak <kzak@redhat.com> 2.23.2-27 +- fix #1301091 - [libblkid] Failed to get offset of the xfs_external_log signature + +* Fri Aug 21 2015 Karel Zak <kzak@redhat.com> 2.23.2-26 +- fix #1182831 - blkid incorrectly detects boot sec + MBR as FAT + +* Wed Aug 12 2015 Karel Zak <kzak@redhat.com> 2.23.2-25 +- fix #1251250 - blkid outputs nothing for some partitions (e.g. sun label) + +* Thu Jul 2 2015 Karel Zak <kzak@redhat.com> 2.23.2-24 +- fix #1199619 - os-prober attempts to mount thin pool devices + +* Tue Jun 23 2015 Karel Zak <kzak@redhat.com> 2.23.2-23 +- fix #1225235 - blockdev --report fails on loop and pmem devices +- fix #1225053 - [RFE] backport fstrim -a and unit file +- fix #1131523 - mount does not appear to be correctly documented for default mount options +- fix #1147526 - Incorrect message printed when su is killed +- fix #1165702 - make login(1) insensitive to SIGXFSZ and huge lastlog +- fix #1184558 - logger silently changes facility from kern to user +- fix #1198243 - blkid does not recognize xfs external log device +- fix #1208081 - lsblk does not list dm-multipath and partitioned devices +- fix #1209594 - RHEL7: unshare: add --propagation, use MS_PRIVATE by default +- fix #1229436 - Remove patch that disables User Namespaces +- fix #1116100 - Add -Z SELInux option to nsenter + +* Mon Apr 20 2015 Karel Zak <kzak@redhat.com> 2.23.2-22 +- fix #1092039 - uuidd not configured to run permanently + +* Tue Jan 13 2015 Karel Zak <kzak@redhat.com> 2.23.2-21 +- fix #1181444 - raw command fails with "Cannot locate raw device" + +* Fri Dec 12 2014 Karel Zak <kzak@redhat.com> 2.23.2-20 +- fix lslogins patch (#1113043) + +* Mon Oct 27 2014 Karel Zak <kzak@redhat.com> 2.23.2-19 +- fix #1156352 - blkdiscard progress report and interruptibility of the process + +* Fri Oct 10 2014 Karel Zak <kzak@redhat.com> 2.23.2-18 +- fix #1149278 - partx fails to update a specific partition other than the first + +* Thu Sep 25 2014 Karel Zak <kzak@redhat.com> 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 <kzak@redhat.com> 2.23.2-16 +- fix bugs in patch for #1047376 + +* Tue Mar 25 2014 Karel Zak <kzak@redhat.com> 2.23.2-15 +- fix #1078618 - flock nfs file fails on nfsv4 +- fix #1047376 - blkid hangs while reading from /dev/fd0 +- fix #1079931 - fsck: warning on kdump boot + +* Wed Mar 12 2014 Karel Zak <kzak@redhat.com> 2.23.2-14 +- fix #1073851 - disable user namespaces at all + +* Thu Feb 20 2014 Karel Zak <kzak@redhat.com> 2.23.2-13 +- fix #1061751 - nsenter set uid for user namespaces + +* Fri Jan 24 2014 Daniel Mach <dmach@redhat.com> - 2.23.2-12 +- Mass rebuild 2014-01-24 + +* Thu Jan 23 2014 Karel Zak <kzak@redhat.com> 2.23.2-11 +- fix #1046849 - filesystem type is not correctly displayed by df command +- fix #1055490 - libblkid: Do not problem for backup btrfs superblock +- fix #1054186 - wipefs does not clean gpt header fully, PMBR remains + +* Tue Jan 14 2014 Karel Zak <kzak@redhat.com> 2.23.2-10 +- sort NOMA nodes in lscpu(1) output to improve fix for #1050852 + +* Tue Jan 14 2014 Karel Zak <kzak@redhat.com> 2.23.2-9 +- fix #1039189 - taskset man page PERMISSIONS section is incorrect +- fix #1050852 - lscpu: support discontinuous NUMA nodes + +* Fri Dec 27 2013 Daniel Mach <dmach@redhat.com> - 2.23.2-8 +- Mass rebuild 2013-12-27 + +* Fri Nov 15 2013 Karel Zak <kzak@redhat.com> 2.23.2-7 +- fix #1024366 - losetup does not use loop-control to add device +- fix #1016471 - document that blockdev --setbsz call has never worked + +* Mon Oct 7 2013 Karel Zak <kzak@redhat.com> 2.23.2-6 +- fix #1010193 - libmount umount issues +- fix #1009349 - Joking sfdisk rewriting PT after "n" +- fix #1007885 - utmpdump is not IPv6 ready + +* Thu Sep 12 2013 Karel Zak <kzak@redhat.com> 2.23.2-5 +- fix #1006462 - RFE: allow \S escape that defaults to PRETTY_NAME in /etc/issue handling + +* Mon Sep 9 2013 Karel Zak <kzak@redhat.com> 2.23.2-4 +- fix #1005566 - recount_geometry: Process /usr/sbin/fdisk was killed by signal 8 (SIGFPE) +- fix #1005194 - su generates incorrect log entries + +* Mon Sep 9 2013 Karel Zak <kzak@redhat.com> 2.23.2-3 +- refresh and rename patches +- fix #987787 - Remove lastlogin from su +- fix #950497 - problem umounting loop device +- fix #921498 - multiple internal testsuite failures + +* Thu Aug 1 2013 Karel Zak <kzak@redhat.com> 2.23.2-2 +- fix 990083 - su doesn't work with pam_ecryptfs + +* Wed Jul 31 2013 Karel Zak <kzak@redhat.com> 2.23.2-1 +- upgrade to stable release 2.23.2 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.23/v2.23.2-ReleaseNotes + +* Thu Jun 13 2013 Karel Zak <kzak@redhat.com> 2.23.1-3 +- fix #972457 - agetty idle I/O polling causes elevated CPU usage + +* Wed Jun 5 2013 Karel Zak <kzak@redhat.com> 2.23.1-2 +- fix #962145 - in.telnetd immediately closes connection + +* Tue May 28 2013 Karel Zak <kzak@redhat.com> 2.23.1-1 +- upgrade to 2.23.1 +- backport agetty --local-line path + +* Thu Apr 25 2013 Karel Zak <kzak@redhat.com> 2.23-1 +- upgrade to 2.23 +- add --with check to call make check + +* Mon Apr 15 2013 Karel Zak <kzak@redhat.com> 2.23-0.7 +- remove unused patches + +* Mon Apr 15 2013 Karel Zak <kzak@redhat.com> 2.23-0.6 +- remove floppy from util-linux + +* Fri Apr 12 2013 Karel Zak <kzak@redhat.com> 2.23-0.5 +- fix #948274 - interruption code 0x4003B in libmount.so.1.1.0 + +* Wed Apr 10 2013 Karel Zak <kzak@redhat.com> 2.23-0.4 +- upgrade to the release 2.23-rc2 + +* Wed Mar 27 2013 Karel Zak <kzak@redhat.com> 2.23-0.3 +- libblkid ntfs bugfix for build on s390 + +* Wed Mar 27 2013 Karel Zak <kzak@redhat.com> 2.23-0.2 +- add upstream patches for to fix umount and mount.<type> + +* Fri Mar 22 2013 Karel Zak <kzak@redhat.com> 2.23-0.1 +- upgrade to the release 2.23-rc1 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.23/v2.23-ReleaseNotes +- add nsenter and blkdiscard +- remove tunelp + +* Wed Feb 20 2013 Karel Zak <kzak@redhat.com> 2.22.2-6 +- fix #912778 - "runuser -l" doesn't register session to systemd + +* Tue Feb 19 2013 Karel Zak <kzak@redhat.com> 2.22.2-5 +- fix #902512 - Dependency failed for /home (and blkid fails to tell UUID) +- refresh old patches + +* Wed Feb 6 2013 Karel Zak <kzak@redhat.com> 2.22.2-4 +- improve convertion to mtab symlink in post script +- spec file cleanup (based on #894199) + +* Sun Feb 3 2013 Karel Zak <kzak@redhat.com> 2.22.2-3 +- fix #882305 - agetty: unstable /dev/tty* permissions +- fix #885314 - hexdump segfault +- fix #896447 - No newlines in piped "cal" command +- fix libblkid cache usage (upstream patch) +- fix #905008 - uuidd: /usr/sbin/uuidd has incorrect file permissions + +* Tue Jan 15 2013 Karel Zak <kzak@redhat.com> 2.22.2-2 +- fix #889888 - wipefs does not completely wipe btrfs volume + +* Thu Dec 13 2012 Karel Zak <kzak@redhat.com> 2.22.2-1 +- upgrade to upstream maintenance release 2.22.2 + +* Mon Nov 19 2012 Karel Zak <kzak@redhat.com> 2.22.1-5 +- sources cleanup + +* Fri Nov 16 2012 Karel Zak <kzak@redhat.com> 2.22.1-4 +- fix #872787 - su: COMMAND not specified + +* Thu Nov 1 2012 Karel Zak <kzak@redhat.com> 2.22.1-3 +- backport upstream runuser(1) +- enable su(1) + +* Thu Nov 1 2012 Karel Zak <kzak@redhat.com> 2.22.1-2 +- apply pathes from upstream stable/v2.22 branch +- fix #865961 - wipefs -a should use O_EXCL + +* Thu Oct 10 2012 Karel Zak <kzak@redhat.com> 2.22.1-1 +- upgrade to the release 2.22.1 + +* Wed Oct 3 2012 Karel Zak <kzak@redhat.com> 2.22-2 +- remove obsolete references to e2fsprogs + +* Thu Sep 6 2012 Karel Zak <kzak@redhat.com> 2.22-1 +- upgrade to the release 2.22 +- enable eject(1) from util-linux, obsolete original eject package +- fix #853164 - setuid program should have full RELRO +- fix #851230 - probe_ntfs: /usr/sbin/blkid was killed by SIGSEGV + +* Thu Aug 16 2012 Karel Zak <kzak@redhat.com> 2.22-0.1 +- upgrade to the release 2.22-rc2 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.22/v2.22-ReleaseNotes +- add sulogin, utmpdump, lslocks, wdctl + +* Fri Jul 27 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.21.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Wed Jun 13 2012 Karel Zak <kzak@redhat.com> 2.21.2-2 +- replace udev dependenceis with systemd + +* Fri May 25 2012 Karel Zak <kzak@redhat.com> 2.21.2-1 +- upgrade to bugfix release 2.21.2 +- fix #814699 - namei(1) incorrectly resolves relative symlinks +- fix #820707 - Impossible to unmount nfsv4/krb5 mounts after network disconnect +- fix #816877 - libmount does not close device fd before mount(2) +- fix #822705 - unable to login after installing + +* Fri Mar 30 2012 Karel Zak <kzak@redhat.com> 2.21.1-1 +- upgrade to bugfix release 2.21.1 + +* Fri Feb 24 2012 Karel Zak <kzak@redhat.com> 2.21-1 +- upgrade to release 2.21 + +* Thu Feb 09 2012 Karel Zak <kzak@redhat.com> 2.21-0.2 +- fix #788703 - /run/blkid does not exist + +* Thu Feb 07 2012 Karel Zak <kzak@redhat.com> 2.21-0.1 +- upgrade to the release 2.21-rc2 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.21/v2.21-ReleaseNotes +- add {fsck,mkfs}.minix +- add new command chcpu(8) +- add new command prlimit(1) +- enable raw(8) command +- move 60-raw.rules from /etc from /usr/lib/udev/rules.d +- move blkid cache from etc to /run/blkid + +* Wed Jan 25 2012 Harald Hoyer <harald@redhat.com> 2.20.1-5 +- install everything in /usr + https://fedoraproject.org/wiki/Features/UsrMove + +* Sat Jan 14 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.20.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Tue Nov 22 2011 Karel Zak <kzak@redhat.com> 2.20.1-3 +- fix #748216 - util-linux requires pam >= 1.1.3-7 +- remove ddate(1) + +* Wed Oct 26 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.20.1-2 +- Rebuilt for glibc bug#747377 + +* Thu Oct 20 2011 Karel Zak <kzak@redhat.com> 2.20.1-1 +- upgrade to the release 2.20.1 + ftp://ftp.infradead.org/pub/util-linux/v2.20/v2.20.1-ReleaseNotes + +* Mon Aug 29 2011 Karel Zak <kzak@redhat.com> 2.20-1 +- upgrade to the release 2.20 + +* Wed Aug 17 2011 Karel Zak <kzak@redhat.com> 2.20-0.2 +- upgrade to the release 2.20-rc2 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.20/v2.20-rc2-ChangeLog + +* Fri Aug 2 2011 Karel Zak <kzak@redhat.com> 2.20-0.1 +- upgrade to the release 2.20-rc1 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.20/v2.20-ReleaseNotes + +* Mon Jul 4 2011 Karel Zak <kzak@redhat.com> 2.19.1-2 +- fix #716483 - /var/tmp --(BIND-mounted)--> /tmp disrupts/hangs bootup +- fix #709681 - failure to mount if a mount point ends with a slash in /etc/fstab +- fix #709319 - 'mount -a' mounts already mounted directories +- fix kernel version parsing + +* Fri May 6 2011 Karel Zak <kzak@redhat.com> 2.19.1-1 +- upgrade to the release 2.19.1 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.19/v2.19.1-ReleaseNotes + +* Wed Apr 20 2011 Karel Zak <kzak@redhat.com> 2.19.1-0.1 +- upgrade to the release 2.19.1-rc1 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.19/v2.19.1-rc1-ChangeLog + +* Mon Mar 7 2011 Karel Zak <kzak@redhat.com> 2.19-2 +- fix #682502 - Broken source URL to floppy tarball, new version available +- upgrade to floppy-0.18 + +* Thu Feb 10 2011 Karel Zak <kzak@redhat.com> 2.19-1 +- upgrade to the release 2.19 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.19/v2.19-ReleaseNotes +- remove /sbin/mount.tmpfs -- integrated to mount(8) + +* Tue Feb 8 2011 Karel Zak <kzak@redhat.com> 2.19-0.6 +- fix #665062 - add support for the postlogin PAM stack to util-linux-ng + +* Mon Feb 07 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.19-0.5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Tue Jan 25 2011 Karel Zak <kzak@redhat.com> 2.19-0.4 +- upgrade to the release 2.19-rc3 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.19/v2.19-rc3-ChangeLog + +* Tue Jan 25 2011 Karel Zak <kzak@redhat.com> 2.19-0.3 +- upgrade to the release 2.19-rc2 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.19/v2.19-rc2-ChangeLog +- fix #671893 - SELinux is preventing /bin/chown from 'setattr' accesses + on the file mounts. + +* Wed Jan 19 2011 Karel Zak <kzak@redhat.com> 2.19-0.2 +- clean up specfile (review #667416) + +* Wed Jan 5 2011 Karel Zak <kzak@redhat.com> 2.19-0.1 +- upgrade to the release 2.19-rc1 + ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.19/v2.19-ReleaseNotes + +* Tue Oct 26 2010 Karel Zak <kzak@redhat.com> 2.18-5 +- fix #645640 - new "-s" parameter parsing in agetty does not work +- add -l (lock) support to fsck + +* Wed Aug 18 2010 Karel Zak <kzak@redhat.com> 2.18-4 +- fix #623685 - please extend agetty to not require a baud rate to be specified + +* Thu Aug 5 2010 Karel Zak <kzak@redhat.com> 2.18-3 +- fix #620924 - /sbin/mount.tmpfs uses not available /usr/bin/id + +* Mon Aug 2 2010 Karel Zak <kzak@redhat.com> 2.18-2 +- fix #615719 - tmpfs mount fails with 'user' option. +- fix #598631 - shutdown, reboot, halt and C-A-D don't work +- fix #618957 - ISO images listed in fstab are mounted twice at boot + +* Wed Jun 30 2010 Karel Zak <kzak@redhat.com> 2.18-1 +- upgrade to the final 2.18 + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.18/v2.18-ReleaseNotes + +* Fri Jun 18 2010 Karel Zak <kzak@redhat.com> 2.18-0.2 +- upgrade to 2.18-rc2 + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.18/v2.18-rc2-ChangeLog + +* Tue Jun 8 2010 Karel Zak <kzak@redhat.com> 2.18-0.1 +- upgrade to the release 2.18-rc1 + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.18/v2.18-ReleaseNotes + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.18/v2.18-rc1-ChangeLog + +* Mon Apr 12 2010 Karel Zak <kzak@redhat.com> 2.17.2-1 +- fix #581252 - remounting tmpfs fails because of hidden rootcontext= +- fix #580296 - "rtcwake" does miss the "off" option +- fix #575734 - use microsecond resolution for blkid cache entries +- upgrade to the bugfix release 2.17.2 + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.17/v2.17.2-ReleaseNotes + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.17/v2.17.2-ChangeLog +- minor fixed in spec file + +* Thu Mar 11 2010 Karel Zak <kzak@redhat.com> 2.17.1-2 +- fix #533874 - libblkid should allow scanning of slow devices (eg. cdroms) + +* Mon Feb 22 2010 Karel Zak <kzak@redhat.com> 2.17.1-1 +- upgrade to the final 2.17.1 + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.17/v2.17.1-ReleaseNotes + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.17/v2.17.1-ChangeLog + +* Tue Feb 16 2010 Karel Zak <kzak@redhat.com> 2.17.1-0.1 +- upgrade to 2.17.1-rc1 + +* Tue Feb 16 2010 Karel Zak <kzak@redhat.com> 2.17-4 +- fix uuidd init script + +* Fri Feb 12 2010 Karel Zak <kzak@redhat.com> 2.17-3 +- fix #541402 - uuidd initscript lsb compliance + +* Fri Jan 8 2010 Karel Zak <kzak@redhat.com> 2.17-2 +- remove Provides: lib{uuid,blkid}-static (thanks to Michael Schwendt) +- remove useless URL to sf.net + +* Thu Jan 8 2010 Karel Zak <kzak@redhat.com> 2.17-1 +- upgrade to the final 2.17 + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.17/v2.17-ReleaseNotes + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.17/v2.17-ChangeLog +- fix #545147 - util-linux-ng : Violation of the Packaging Guidelines + (remove uuid and blkid static libs) + +* Mon Dec 14 2009 Karel Zak <kzak@redhat.com> 2.17-0.6 +- minor fixes in spec file (fix URL, add Requires, add LGPLv2+) + +* Wed Dec 9 2009 Karel Zak <kzak@redhat.com> 2.17-0.5 +- upgrade to 2.17-rc2 + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.17/v2.17-rc2-ChangeLog + +* Mon Dec 7 2009 Karel Zak <kzak@redhat.com> 2.17-0.4 +- add clock.8 man page (manlink to hwclock) +- add --help to mount.tmpfs + +* Mon Nov 23 2009 Karel Zak <kzak@redhat.com> 2.17-0.3 +- upgrade to 2.17-rc1 + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.17/v2.17-rc1-ChangeLog + +* Tue Nov 10 2009 Karel Zak <kzak@redhat.com> 2.17-0.2.git10dfc39 +- upgrade to pre-release snapshot (official changelog not available yet, see + http://git.kernel.org/?p=utils/util-linux-ng/util-linux-ng.git for now) + +* Mon Oct 19 2009 Karel Zak <kzak@redhat.com> 2.17-0.1.git5e51568 +- upgrade to pre-release snapshot (official changelog not available yet, see + http://git.kernel.org/?p=utils/util-linux-ng/util-linux-ng.git for now) +- new commands: fallocate, unshare, wipefs +- libblkid supports topology and partitions probing +- remove support for --rmpart[s] from blockdev(8) (util-linux-ng-2.14-blockdev-rmpart.patch) +- merged upstream: + util-linux-ng-2.14-sfdisk-dump.patch + util-linux-ng-2.16-blkid-swsuspend.patch + util-linux-ng-2.16-libblkid-compression.patch + util-linux-ng-2.16-libblkid-ext2.patch + util-linux-ng-2.16-switchroot-tty.patch + +* Mon Oct 5 2009 Karel Zak <kzak@redhat.com> 2.16-13 +- fix spec file + +* Fri Oct 2 2009 Karel Zak <kzak@redhat.com> 2.16-12 +- release++ + +* Thu Oct 1 2009 Karel Zak <kzak@redhat.com> 2.16-11 +- fix #519237 - bash: cannot set terminal process group (-1): Inappropriate ioctl for device + +* Wed Sep 16 2009 Tomas Mraz <tmraz@redhat.com> - 2.16-10 +- use password-auth common PAM configuration instead of system-auth and + drop pam_console.so call from the remote PAM config file + +* Mon Sep 14 2009 Karel Zak <kzak@redhat.com> 2.16-9 +- fix #522718 - sfdisk -d /dev/xxx | sfdisk --force /dev/yyy fails when LANG is set +- fix typo in swsuspend detection + +* Wed Aug 26 2009 Tomas Mraz <tmraz@redhat.com> - 2.16-8 +- rebuilt with new audit + +* Sun Aug 23 2009 Karel Zak <kzak@redhat.com> 2.16-7 +- fix #518572 - blkid requires ext2.ko to be decompressed on installation media + +* Thu Aug 13 2009 Karel Zak <kzak@redhat.com> 2.16-5 +- fix #513104 - blkid returns no fstype for ext2 device when ext2 module not loaded + +* Wed Aug 5 2009 Stepan Kasal <skasal@redhat.com> 2.16-4 +- set conflict with versions of e2fsprogs containing fsck + +* Thu Jul 30 2009 Karel Zak <kzak@redhat.com> 2.16-3 +- remove the mount.conf support (see #214891) + +* Mon Jul 27 2009 Karel Zak <kzak@redhat.com> 2.16-2 +- fix #214891 - add mount.conf and MTAB_LOCK_DIR= option + +* Sat Jul 25 2009 Karel Zak <kzak@redhat.com> 2.16-1 +- upgrade to 2.16 + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.16/v2.16-ReleaseNotes +- enable built-in libuuid (replacement for the old uuid stuff from e2fsprogs) +- new commands switch_root, uuidgen and uuidd (subpackage) + +* Wed Jun 10 2009 Karel Zak <kzak@redhat.com> 2.15.1-1 +- upgrade to 2.15.1 + +* Mon Jun 8 2009 Karel Zak <kzak@redhat.com> 2.15.1-0.2 +- set BuildRequires: e2fsprogs-devel +- add Requires: e2fsprogs-devel to libblkid-devel + +* Thu Jun 4 2009 Karel Zak <kzak@redhat.com> 2.15.1-0.1 +- upgrade to 2.15.1-rc1 + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.15/v2.15-ReleaseNotes + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.15/v2.15.1-rc1-ChangeLog +- merged patches: + util-linux-ng-2.14-login-remote.patch + util-linux-ng-2.14-fdisk-4k-I.patch + util-linux-ng-2.14-fdisk-4k-II.patch + util-linux-ng-2.14-fdisk-4k-III.patch + util-linux-ng-2.14-dmesg-r.patch + util-linux-ng-2.14-flock-segfaults.patch + util-linux-ng-2.14-renice-n.patch +- new commands: lscpu, ipcmk +- remove support for "managed" and "kudzu" mount options +- cleanup spec file +- enable built-in libblkid (replacement for the old blkid from e2fsprogs) + +* Thu Apr 2 2009 Karel Zak <kzak@redhat.com> 2.14.2-8 +- fix #490769 - post scriptlet failed (thanks to Dan Horak) + +* Fri Mar 20 2009 Karel Zak <kzak@redhat.com> 2.14.2-7 +- fix some nits in mount.tmpfs + +* Fri Mar 20 2009 Karel Zak <kzak@redhat.com> 2.14.2-6 +- fix #491175 - mount of tmpfs FSs fail at boot + +* Thu Mar 19 2009 Karel Zak <kzak@redhat.com> 2.14.2-5 +- fix #489672 - flock segfaults when file name is not given (upstream) +- fix #476964 - Mount /var/tmp with tmpfs creates denials +- fix #487227 - fdisk 4KiB hw sectors support (upstream) +- fix #477303 - renice doesn't support -n option (upstream) + +* Wed Feb 25 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.14.2-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Fri Feb 20 2009 Karel Zak <kzak@redhat.com> 2.14.2-3 +- add -r option to dmesg(1) + +* Mon Feb 9 2009 Karel Zak <kzak@redhat.com> 2.14.2-2 +- fix typo in spec file + +* Mon Feb 9 2009 Karel Zak <kzak@redhat.com> 2.14.2-1 +- upgrade to stable 2.14.2 + ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/v2.14/v2.14.2-ReleaseNotes + +* Thu Jan 22 2009 Karel Zak <kzak@redhat.com> 2.14.2-0.2 +- fix #480413 - util-linux-ng doesn't include scriptreplay +- fix #479002 - remove dependency on ConsoleKit-libs +- upgrade to 2.14.2-rc2 + +* Mon Dec 22 2008 Karel Zak <kzak@redhat.com> 2.14.2-0.1 +- upgrade to 2.14.2-rc1 +- refresh old patches + +* Fri Nov 21 2008 Karel Zak <kzak@redhat.com> 2.14.1-5 +- fix #472502 - problem with fdisk and use +sectors for the end of partition + +* Mon Oct 6 2008 Karel Zak <kzak@redhat.com> 2.14.1-3 +- fix #465761 - mount manpage is missing uid/gid mount options for tmpfs +- refresh util-linux-ng-2.14-mount-file_t.patch (fuzz=0) + +* Wed Sep 10 2008 Karel Zak <kzak@redhat.com> 2.14.1-2 +- remove obsolete pam-console support + +* Wed Sep 10 2008 Karel Zak <kzak@redhat.com> 2.14.1-1 +- upgrade to stable 2.14.1 + +* Thu Aug 14 2008 Karel Zak <kzak@redhat.com> 2.14.1-0.1 +- upgrade to 2.14.1-rc1 +- refresh old patches + +* Thu Jul 24 2008 Karel Zak <kzak@redhat.com> 2.14-3 +- update util-linux-ng-2.14-mount-file_t.patch to make + the SELinux warning optional (verbose mode is required) + +* Tue Jul 1 2008 Karel Zak <kzak@redhat.com> 2.14-2 +- fix #390691 - mount should check selinux context on mount, and warn on file_t + +* Mon Jun 9 2008 Karel Zak <kzak@redhat.com> 2.14-1 +- upgrade to stable util-linux-ng release + +* Mon May 19 2008 Karel Zak <kzak@redhat.com> 2.14-0.1 +- upgrade to 2.14-rc3 +- remove arch(8) (deprecated in favor of uname(1) or arch(1) from coreutils) +- add a new command ldattach(8) +- cfdisk(8) linked with libncursesw + +* Tue Apr 22 2008 Karel Zak <kzak@redhat.com> 2.13.1-9 +- fix audit log injection attack via login + +* Thu Apr 17 2008 Karel Zak <kzak@redhat.com> 2.13.1-8 +- fix location of the command raw(8) + +* Tue Apr 15 2008 Karel Zak <kzak@redhat.com> 2.13.1-7 +- fix 244383 - libblkid uses TYPE="swsuspend" for S1SUSPEND/S2SUSPEND + +* Wed Apr 2 2008 Karel Zak <kzak@redhat.com> 2.13.1-6 +- fix 439984 - backport mkswap -U + +* Wed Mar 26 2008 Tom "spot" Callaway <tcallawa@redhat.com> - 2.13.1-5 +- clean up sparc conditionals + +* Tue Feb 19 2008 Fedora Release Engineering <rel-eng@fedoraproject.org> - 2.13.1-4 +- Autorebuild for GCC 4.3 + +* Mon Jan 28 2008 Karel Zak <kzak@redhat.com> 2.13.1-3 +- upgrade to new upstream release +- fix #427874 - util-linux-ng gets "excess command line argument" on update + +* Wed Jan 2 2008 Karel Zak <kzak@redhat.com> 2.13.1-2 +- update to upstream 2.13.1-rc2 + +* Wed Dec 12 2007 Dan Walsh <dwalsh@redhat.com> 2.13.1-1 +- Fix pam files so that pam_keyinit happens after pam_selinux.so + +* Wed Dec 12 2007 Karel Zak <kzak@redhat.com> 2.13.1-0.2 +- remove viwp and vigr (in favour of shadow-utils) + +* Sun Dec 9 2007 Karel Zak <kzak@redhat.com> 2.13.1-0.1 +- update to the latest upstream stable branch + (commit: fda9d11739ee88c3b2f22a73f12ec019bd3b8335) + +* Wed Oct 31 2007 Karel Zak <kzak@redhat.com> 2.13-4 +- fix #354791 - blockdev command calls the blkpg ioctl with a wrong data structure + +* Tue Oct 16 2007 Karel Zak <kzak@redhat.com> 2.13-3 +- fix mount -L | -U segfault +- fix script die on SIGWINCH + +* Thu Oct 4 2007 Karel Zak <kzak@redhat.com> 2.13-2 +- update to the latest upstream stable branch + +* Tue Aug 28 2007 Karel Zak <kzak@redhat.com> 2.13-1 +- upgrade to stable util-linux-ng release + +* Fri Aug 24 2007 Karel Zak <kzak@redhat.com> 2.13-0.59 +- add release number to util-linux Provides and increment setarch Obsoletes +- fix #254114 - spec typo +- upgrade to floppy-0.16 +- add BuildRequires: popt-devel + +* Wed Aug 22 2007 Jesse Keating <jkeating@redhat.com> 2.13-0.58 +- Obsolete a sufficiently high enough version of setarch + +* Mon Aug 20 2007 Karel Zak <kzak@redhat.com> 2.13-0.57 +- fix #253664 - util-linux-ng fails to build on sparc (patch by Dennis Gilmore) +- rebase to new GIT snapshot + +* Mon Aug 20 2007 Karel Zak <kzak@redhat.com> 2.13-0.56 +- fix obsoletes field + +* Mon Aug 20 2007 Karel Zak <kzak@redhat.com> 2.13-0.55 +- util-linux-ng includes setarch(1), define relevat Obsoletes+Provides + +* Mon Aug 20 2007 Karel Zak <kzak@redhat.com> 2.13-0.54 +- port "blockdev --rmpart" patch from util-linux +- use same Provides/Obsoletes setting like in util-linux + +* Wed Aug 15 2007 Karel Zak <kzak@redhat.com> 2.13-0.53 +- fix #252046 - review Request: util-linux-ng (util-linux replacement) + +* Mon Aug 13 2007 Karel Zak <kzak@redhat.com> 2.13-0.52 +- rebase to util-linux-ng (new util-linux upstream fork, + based on util-linux 2.13-pre7) +- more than 70 Fedora/RHEL patches have been merged to upstream code + +* Fri Apr 6 2007 Karel Zak <kzak@redhat.com> 2.13-0.51 +- fix #150493 - hwclock --systohc sets clock 0.5 seconds slow +- fix #220873 - starting RPC idmapd: Error: RPC MTAB does not exist. + (added rpc_pipefs to util-linux-2.13-umount-sysfs.patch) +- fix #227903 - mount -f does not work with NFS-mounted + +* Sat Mar 3 2007 David Zeuthen <davidz@redhat.com> 2.13-0.50 +- include ConsoleKit session module by default (#229172) + +* Thu Jan 11 2007 Karel Zak <kzak@redhat.com> 2.13-0.49 +- fix #222293 - undocumented partx,addpart, delpart + +* Sun Dec 17 2006 Karel Zak <kzak@redhat.com> 2.13-0.48 +- fix paths in po/Makefile.in.in + +* Fri Dec 15 2006 Karel Zak <kzak@redhat.com> 2.13-0.47 +- fix #217240 - namei ignores non-directory components instead of saying "Not a directory" +- fix #217241 - namei enforces symlink limits inconsistently + +* Wed Dec 14 2006 Karel Zak <kzak@redhat.com> 2.13-0.46 +- fix leaking file descriptor in the more command (patch by Steve Grubb) + +* Wed Dec 13 2006 Karel Zak <kzak@redhat.com> 2.13-0.45 +- use ncurses only +- fix #218915 - fdisk -b 4K +- upgrade to -pre7 release +- fix building problem with raw0 patch +- fix #217186 - /bin/sh: @MKINSTALLDIRS@: No such file or directory + (port po/Makefile.in.in from gettext-0.16) +- sync with FC6 and RHEL5: +- fix #216489 - SCHED_BATCH option missing in chrt +- fix #216712 - issues with raw device support ("raw0" is wrong device name) +- fix #216760 - mount with context or fscontext option fails + (temporarily disabled the support for additional contexts -- not supported by kernel yet) +- fix #211827 - Can't mount with additional contexts +- fix #213127 - mount --make-unbindable does not work +- fix #211749 - add -r option to losetup to create a read-only loop + +* Thu Oct 12 2006 Karel Zak <kzak@redhat.com> 2.13-0.44 +- fix #209911 - losetup.8 updated (use dm-crypt rather than deprecated cryptoloop) +- fix #210338 - spurious error from '/bin/login -h $PHONENUMBER' (bug in IPv6 patch) +- fix #208634 - mkswap "works" without warning on a mounted device + +* Sun Oct 01 2006 Jesse Keating <jkeating@redhat.com> - 2.13-0.43 +- rebuilt for unwind info generation, broken in gcc-4.1.1-21 + +* Wed Sep 20 2006 Karel Zak <kzak@redhat.com> 2.13-0.42 +- remove obsolete NFS code and patches (we use /sbin/mount.nfs + and /sbin/umount.nfs from nfs-utils now) +- move nfs.5 to nfs-utils + +* Fri Sep 15 2006 Karel Zak <kzak@redhat.com> 2.13-0.41 +- fix #205038 - mount not allowing sloppy option (exports "-s" + to external /sbin/mount.nfs(4) calls) +- fix minor bug in util-linux-2.13-mount-twiceloop.patch +- fix #188193- util-linux should provide plugin infrastructure for HAL + +* Mon Aug 21 2006 Karel Zak <kzak@redhat.com> 2.13-0.40 +- fix Makefile.am in util-linux-2.13-mount-context.patch +- fix #201343 - pam_securetty requires known user to work + (split PAM login configuration to two files) +- fix #203358 - change location of taskset binary to allow for early affinity work + +* Fri Aug 11 2006 Karel Zak <kzak@redhat.com> 2.13-0.39 +- fix #199745 - non-existant simpleinit(8) mentioned in ctrlaltdel(8) + +* Thu Aug 10 2006 Dan Walsh <dwalsh@redhat.com> 2.13-0.38 +- Change keycreate line to happen after pam_selinux open call so it gets correct context + +* Thu Aug 10 2006 Karel Zak <kzak@redhat.com> 2.13-0.37 +- fix #176494 - last -i returns strange IP addresses (patch by Bill Nottingham) + +* Thu Jul 27 2006 Karel Zak <kzak@redhat.com> 2.13-0.36 +- fix #198300, #199557 - util-linux "post" scriptlet failure + +* Thu Jul 27 2006 Steve Dickson <steved@redhat.com> 2.13-0.35 +- Added the -o fsc flag to nfsmount. + +* Wed Jul 26 2006 Karel Zak <kzak@redhat.com> 2.13-0.34 +- rebuild + +* Tue Jul 18 2006 Karel Zak <kzak@redhat.com> 2.13-0.33 +- add Requires(post): libselinux + +* Mon Jul 17 2006 Karel Zak <kzak@redhat.com> 2.13-0.32 +- add IPv6 support to the login command (patch by Milan Zazrivec) +- fix #198626 - add keyinit instructions to the login PAM script + (patch by David Howells) + +* Wed Jul 12 2006 Jesse Keating <jkeating@redhat.com> - 2.13-0.31.1 +- rebuild + +* Tue Jul 11 2006 Karel Zak <kzak@redhat.com> 2.13-0.31 +- cleanup dependences for post and preun scriptlets + +* Mon Jul 10 2006 Karsten Hopp <karsten@redhat.de> 2.13-0.30 +- silence install in minimal buildroot without /var/log + +* Fri Jul 7 2006 Karel Zak <kzak@redhat.com> 2.13-0.29 +- include the raw command for RHELs + +* Mon Jun 26 2006 Florian La Roche <laroche@redhat.com> 2.13-0.28 +- move install-info parts from postun to preun + +* Wed Jun 21 2006 Dan Walsh <dwalsh@RedHat.com> 2.13-0.27 +- Only execute chcon on machines with selinux enabled + +* Wed Jun 14 2006 Steve Dickson <steved@redhat.com> 2.13-0.26 +- Remove unneeded header files from nfsmount.c + +* Mon Jun 12 2006 Karel Zak <kzak@redhat.com> 2.13-0.25 +- fix #187014 - umount segfaults for normal user +- fix #183446 - cal not UTF-8-aware +- fix #186915 - mount does not translate SELIinux context options though libselinux +- fix #185500 - Need man page entry for -o context= mount option +- fix #152579 - missing info about /etc/mtab and /proc/mounts mismatch +- fix #183890 - missing info about possible ioctl() and fcntl() problems on NFS filesystem +- fix #191230 - using mount --move results in wrong data in /etc/mtab +- added mount subtrees support +- fdisk: wrong number of sectors for large disks (suse#160822) +- merge fdisk-xvd (#182553) with new fdisk-isfull (#188981) patch +- fix #181549 - raw(8) manpage has old information about dd +- remove asm/page.h usage + +* Wed May 24 2006 Dan Walsh <dwalsh@RedHat.com> 2.13-0.24 +- Remove requirement on restorecon, since we can do the same thing +- with chcon/matchpathcon, and not add requirement on policycoreutils + +* Wed May 24 2006 Steve Dickson <steved@redhat.com> 2.13-0.23 +- Fixed bug in patch for bz183713 which cause nfs4 mounts to fail. + +* Tue May 2 2006 Steve Dickson <steved@redhat.com> 2.13-0.22 +- Added syslog logging to background mounts as suggested + by a customer. + +* Mon May 1 2006 Steve Dickson <steved@redhat.com> 2.13-0.21 +- fix #183713 - foreground mounts are not retrying as advertised +- fix #151549 - Added 'noacl' mount flag +- fix #169042 - Changed nfsmount to try udp before using tcp when rpc-ing + the remote rpc.mountd (iff -o tcp is not specified). + This drastically increases the total number of tcp mounts + that can happen at once (ala autofs). + +* Wed Mar 9 2006 Jesse Keating <jkeating@redhat.com> 2.13-0.20 +- Better calling of restorecon as suggested by Bill Nottingham +- prereq restorecon to avoid ordering issues + +* Wed Mar 9 2006 Jesse Keating <jkeating@redhat.com> 2.13-0.19 +- restorecon /var/log/lastlog + +* Wed Mar 8 2006 Karel Zak <kzak@redhat.com> 2.13-0.17 +- fix #181782 - mkswap selinux relabeling (fix util-linux-2.13-mkswap-selinux.patch) + +* Wed Feb 22 2006 Karel Zak <kzak@redhat.com> 2.13-0.16 +- fix #181782 - mkswap should automatically add selinux label to swapfile +- fix #180730 - col is exiting with 1 (fix util-linux-2.12p-col-EILSEQ.patch) +- fix #181896 - broken example in schedutils man pages +- fix #177331 - login omits pam_acct_mgmt & pam_chauthtok when authentication is skipped. +- fix #177523 - umount -a should not unmount sysfs +- fix #182553 - fdisk -l inside xen guest shows no disks + +* Fri Feb 10 2006 Jesse Keating <jkeating@redhat.com> - 2.13-0.15.1 +- bump again for double-long bug on ppc(64) + +* Wed Feb 8 2006 Peter Jones <pjones@redhat.com> 2.13-0.15 +- add "blockdev --rmpart N <device>" and "blockdev --rmparts <device>" + +* Tue Feb 07 2006 Jesse Keating <jkeating@redhat.com> - 2.13-0.14.1 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Thu Jan 19 2006 Steve Dickson <steved@redhat.com> 2.13-0.14 +- Updated the gssd_check() and idmapd_check(), used with + nfsv4 mounts, to looked for the correct file in /var/lock/subsys + which stops bogus warnings. + +* Tue Jan 3 2006 Karel Zak <kzak@redhat.com> 2.13-0.13 +- fix #174676 - hwclock audit return code mismatch +- fix #176441: col truncates data +- fix #174111 - mount allows loopback devices to be mounted more than once to the same mount point +- better wide chars usage in the cal command (based on the old 'moremisc' patch) + +* Mon Dec 12 2005 Karel Zak <kzak@redhat.com> 2.13-0.12 +- rebuilt + +* Fri Dec 09 2005 Jesse Keating <jkeating@redhat.com> +- rebuilt + +* Fri Nov 25 2005 Karel Zak <kzak@redhat.com> 2.13-0.11.pre6 +- update to upstream version 2.13-pre6 +- fix #172203 - mount man page in RHEL4 lacks any info on cifs mount options + +* Mon Nov 7 2005 Karel Zak <kzak@redhat.com> 2.13-0.10.pre5 +- fix #171337 - mkfs.cramfs doesn't work correctly with empty files + +* Fri Oct 28 2005 Karel Zak <kzak@redhat.com> 2.13-0.9.pre5 +- rebuild + +* Wed Oct 26 2005 Karel Zak <kzak@redhat.com> 2.13-0.8.pre5 +- updated version of the patch for hwclock audit + +* Thu Oct 20 2005 Karel Zak <kzak@redhat.com> 2.13-0.7.pre5 +- fix #171337 - mkfs.cramfs dies creating installer image + +* Thu Oct 20 2005 Karel Zak <kzak@redhat.com> 2.13-0.6.pre5 +- update to upstream 2.13pre5 +- remove separated cramfs1.1 (already in upstream package) +- remove odd symlink /usr/bin/mkcramfs -> ../../sbin/mkfs.cramfs +- fix #170171 - ipcs -lm always report "max total shared memory (kbytes) = 0" + +* Mon Oct 17 2005 Karel Zak <kzak@redhat.com> 2.13-0.5.pre4 +* fix #170564 - add audit message to login + +* Fri Oct 7 2005 Karel Zak <kzak@redhat.com> 2.13-0.4.pre4 +- fix #169628 - /usr/bin/floppy doesn't work with /dev/fd0 +- fix #168436 - login will attempt to run if it has no read/write access to its terminal +- fix #168434 - login's timeout can fail - needs to call siginterrupt(SIGALRM,1) +- fix #165253 - losetup missing option -a [new feature] +- update PAM files (replace pam_stack with new "include" PAM directive) +- remove kbdrate from src.rpm +- update to 2.13pre4 + +* Fri Oct 7 2005 Steve Dickson <steved@redhat.com> 2.13-0.3.pre3 +- fix #170110 - Documentation for 'rsize' and 'wsize' NFS mount options + is misleading + +* Fri Sep 2 2005 Karel Zak <kzak@redhat.com> 2.13-0.3.pre2 +- fix #166923 - hwclock will not run on a non audit-enabled kernel +- fix #159410 - mkswap(8) claims max swap area size is 2 GB +- fix #165863 - swsusp swaps should be reinitialized +- change /var/log/lastlog perms to 0644 + +* Tue Aug 16 2005 Karel Zak <kzak@redhat.com> 2.13-0.2.pre2 +- /usr/share/misc/getopt/* -move-> /usr/share/doc/util-linux-2.13/getopt-* +- the arch command marked as deprecated +- removed: elvtune, rescuept and setfdprm +- removed: man8/sln.8 (moved to man-pages, see #10601) +- removed REDAME.pg and README.reset +- .spec file cleanup +- added schedutils (commands: chrt, ionice and taskset) + +* Tue Jul 12 2005 Karel Zak <kzak@redhat.com> 2.12p-9.7 +- fix #159339 - util-linux updates for new audit system +- fix #158737 - sfdisk warning for large partitions, gpt +- fix #150912 - Add ocfs2 support +- NULL is better than zero at end of execl() + +* Thu Jun 16 2005 Karel Zak <kzak@redhat.com> 2.12p-9.5 +- fix #157656 - CRM 546998: Possible bug in vipw, changes permissions of /etc/shadow and /etc/gshadow +- fix #159339 - util-linux updates for new audit system (pam_loginuid.so added to util-linux-selinux.pamd) +- fix #159418 - sfdisk unusable - crashes immediately on invocation +- fix #157674 - sync option on VFAT mount destroys flash drives +- fix .spec file /usr/sbin/{hwclock,clock} symlinks + +* Wed May 4 2005 Jeremy Katz <katzj@redhat.com> - 2.12p-9.3 +- rebuild against new libe2fsprogs (and libblkid) to fix cramfs auto-detection + +* Mon May 2 2005 Karel Zak <kzak@redhat.com> 2.12p-9.2 +- rebuild + +* Mon May 2 2005 Karel Zak <kzak@redhat.com> 2.12p-9 +- fix #156597 - look - doesn't work with separators + +* Mon Apr 25 2005 Karel Zak <kzak@redhat.com> 2.12p-8 +- fix #154498 - util-linux login & pam session +- fix #155293 - man 5 nfs should include vers as a mount option +- fix #76467 - At boot time, fsck chokes on LVs listed by label in fstab +- new Source URL +- added note about ATAPI IDE floppy to fdformat.8 +- fix #145355 - Man pages for fstab and fstab-sync in conflict + +* Tue Apr 5 2005 Karel Zak <kzak@redhat.com> 2.12p-7 +- enable build with libblkid from e2fsprogs-devel +- remove workaround for duplicated labels + +* Thu Mar 31 2005 Steve Dickson <SteveD@RedHat.com> 2.12p-5 +- Fixed nfs mount to rollback correctly. + +* Fri Mar 25 2005 Karel Zak <kzak@redhat.com> 2.12p-4 +- added /var/log/lastlog to util-linux (#151635) +- disabled 'newgrp' in util-linux (enabled in shadow-utils) (#149997, #151613) +- improved mtab lock (#143118) +- fixed ipcs typo (#151156) +- implemented mount workaround for duplicated labels (#116300) + +* Wed Mar 16 2005 Elliot Lee <sopwith@redhat.com> 2.12p-3 +- rebuilt + +* Fri Feb 25 2005 Steve Dickson <SteveD@RedHat.com> 2.12p-2 +- Changed nfsmount to only use reserve ports when necessary + (bz# 141773) + +* Thu Dec 23 2004 Elliot Lee <sopwith@redhat.com> 2.12p-1 +- Update to util-linux-2.12p. This changes swap header format + from - you may need to rerun mkswap if you did a clean install of + FC3. + +* Fri Dec 10 2004 Elliot Lee <sopwith@redhat.com> 2.12j-1 +- Update to util-linux-2.12j + +* Tue Dec 7 2004 Steve Dickson <SteveD@RedHat.com> 2.12a-20 +- Corrected a buffer overflow problem with nfs mounts. + (bz# 141733) + +* Wed Dec 01 2004 Elliot Lee <sopwith@redhat.com> 2.12a-19 +- Patches for various bugs. + +* Mon Nov 29 2004 Steve Dickson <SteveD@RedHat.com> 2.12a-18 +- Made NFS mounts adhere to the IP protocol if specified on + command line as well as made NFS umounts adhere to the + current IP protocol. Fix #140016 + +* Thu Oct 14 2004 Elliot Lee <sopwith@redhat.com> 2.12a-16 +- Add include_raw macro, build with it off for Fedora + +* Wed Oct 13 2004 Stephen C. Tweedie <sct@redhat.com> - 2.12a-15 +- Add raw patch to allow binding of devices not yet in /dev + +* Wed Oct 13 2004 John (J5) Palmieri <johnp@redhat.com> 2.12a-14 +- Add David Zeuthen's patch to enable the pamconsole flag #133941 + +* Wed Oct 13 2004 Stephen C. Tweedie <sct@redhat.com> 2.12a-13 +- Restore raw utils (bugzilla #130016) + +* Mon Oct 11 2004 Phil Knirsch <pknirsch@redhat.com> 2.12a-12 +- Add the missing remote entry in pam.d + +* Wed Oct 6 2004 Steve Dickson <SteveD@RedHat.com> +- Rechecked in some missing NFS mounting code. + +* Wed Sep 29 2004 Elliot Lee <sopwith@redhat.com> 2.12a-10 +- Make swaplabel support work with swapon -a -e + +* Tue Sep 28 2004 Steve Dickson <SteveD@RedHat.com> +- Updated the NFS and NFS4 code to the latest CITI patch set + (in which they incorporate a number of our local patches). + +* Wed Sep 15 2004 Nalin Dahybhai <nalin@redhat.com> 2.12a-8 +- Fix #132196 - turn on SELinux support at build-time. + +* Wed Sep 15 2004 Phil Knirsch <pknirsch@redhat.com> 2.12a-7 +- Fix #91174 with pamstart.patch + +* Tue Aug 31 2004 Elliot Lee <sopwith@redhat.com> 2.12a-6 +- Fix #16415, #70616 with rdevman.patch +- Fix #102566 with loginman.patch +- Fix #104321 with rescuept.patch (just use plain lseek - we're in _FILE_OFFSET_BITS=64 land now) +- Fix #130016 - remove raw. +- Re-add agetty (replacing it with mgetty is too much pain, and mgetty is much larger) + +* Thu Aug 26 2004 Steve Dickson <SteveD@RedHat.com> +- Made the NFS security checks more explicit to avoid confusion + (an upstream fix) +- Also removed a compilation warning + +* Wed Aug 11 2004 Alasdair Kergon <agk@redhat.com> +- Remove unused mount libdevmapper inclusion. + +* Wed Aug 11 2004 Alasdair Kergon <agk@redhat.com> +- Add device-mapper mount-by-label support +- Fix segfault in mount-by-label when a device without a label is present. + +* Wed Aug 11 2004 Steve Dickson <SteveD@RedHat.com> +- Updated nfs man page to show that intr are on by + default for nfs4 + +* Thu Aug 05 2004 Jindrich Novy <jnovy@redhat.com> +- modified warning causing heart attack for >16 partitions, #107824 + +* Fri Jul 09 2004 Elliot Lee <sopwith@redhat.com> 2.12a-3 +- Fix #126623, #126572 +- Patch cleanup +- Remove agetty (use mgetty, agetty is broken) + +* Tue Jun 15 2004 Elliot Lee <sopwith@redhat.com> +- rebuilt + +* Thu Jun 03 2004 Elliot Lee <sopwith@redhat.com> 2.12a-1 +- Update to 2.12a +- Fix #122448 + +* Thu May 13 2004 Dan Walsh <dwalsh@RedHat.com> 2.12-19 +- Change pam_selinux to run last + +* Tue May 04 2004 Elliot Lee <sopwith@redhat.com> 2.12-18 +- Fix #122448 (autofs issues) + +* Fri Apr 23 2004 Elliot Lee <sopwith@redhat.com> 2.12-17 +- Fix #119157 by editing the patch +- Add patch145 to fix #119986 + +* Fri Apr 16 2004 Elliot Lee <sopwith@redhat.com> 2.12-16 +- Fix #118803 + +* Tue Mar 23 2004 Jeremy Katz <katzj@redhat.com> 2.12-15 +- mkcramfs: use PAGE_SIZE for default blocksize (#118681) + +* Sat Mar 20 2004 <SteveD@RedHat.com> +- Updated the nfs-mount.patch to correctly + handle the mounthost option and to ignore + servers that do not set auth flavors + +* Tue Mar 16 2004 Dan Walsh <dwalsh@RedHat.com> 2.12-13 +- Fix selinux ordering or pam for login + +* Tue Mar 16 2004 <SteveD@RedHat.com> +- Make RPC error messages displayed with -v argument +- Added two checks to the nfs4 path what will print warnings + when rpc.idmapd and rpc.gssd are not running +- Ping NFS v4 servers before diving into kernel +- Make v4 mount interruptible which also make the intr option on by default + +* Sun Mar 13 2004 <SteveD@RedHat.com> +- Reworked how the rpc.idmapd and rpc.gssd checks were + done due to review comments from upstream. +- Added rpc_strerror() so the '-v' flag will show RPC errors. + +* Sat Mar 13 2004 <SteveD@RedHat.com> +- Added two checks to the nfs4 path what will print warnings + when rpc.idmapd and rpc.gssd are not running. + +* Thu Mar 11 2004 <SteveD@RedHat.com> +- Reworked and updated the nfsv4 patches. + +* Wed Mar 10 2004 Dan Walsh <dwalsh@RedHat.com> +- Bump version + +* Wed Mar 10 2004 Steve Dickson <SteveD@RedHat.com> +- Tried to make nfs error message a bit more meaninful +- Cleaned up some warnings + +* Sun Mar 7 2004 Steve Dickson <SteveD@RedHat.com> +- Added pesudo flavors for nfsv4 mounts. +- Added BuildRequires: libselinux-devel and Requires: libselinux + when WITH_SELINUX is set. + +* Fri Feb 27 2004 Dan Walsh <dwalsh@redhat.com> 2.12-5 +- check for 2.6.3 kernel in mount options + +* Mon Feb 23 2004 Elliot Lee <sopwith@redhat.com> 2.12-4 +- Remove /bin/kill for #116100 + +* Fri Feb 20 2004 Dan Walsh <dwalsh@redhat.com> 2.12-3 +- rebuilt + +* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com> +- rebuilt + +* Thu Feb 12 2004 Elliot Lee <sopwith@redhat.com> 2.12-1 +- Final 2.12 has been out for ages - might as well use it. + +* Wed Jan 28 2004 Steve Dickson <SteveD@RedHat.com> 2.12pre-4 +- Added mount patches that have NFS version 4 support + +* Mon Jan 26 2004 Elliot Lee <sopwith@redhat.com> 2.12pre-3 +- Provides: mount losetup + +* Mon Jan 26 2004 Dan Walsh <dwalsh@redhat.com> 2.12pre-2 +- Add multiple to /etc/pam.d/login for SELinux + +* Thu Jan 15 2004 Elliot Lee <sopwith@redhat.com> 2.12pre-1 +- 2.12pre-1 +- Merge mount/losetup packages into the main package (#112324) +- Lose separate + +* Mon Nov 3 2003 Dan Walsh <dwalsh@redhat.com> 2.11y-35.sel +- remove selinux code from login and use pam_selinux + +* Thu Oct 30 2003 Dan Walsh <dwalsh@redhat.com> 2.11y-34.sel +- turn on selinux + +* Fri Oct 24 2003 Elliot Lee <sopwith@redhat.com> 2.11y-34 +- Add BuildRequires: texinfo (from a bug# I don't remember) +- Fix #90588 with mountman patch142. + +* Mon Oct 6 2003 Dan Walsh <dwalsh@redhat.com> 2.11y-33 +- turn off selinux + +* Thu Sep 25 2003 Dan Walsh <dwalsh@redhat.com> 2.11y-32.sel +- turn on selinux +- remove context selection + +* Fri Sep 19 2003 Elliot Lee <sopwith@redhat.com> 2.11y-31 +- Add patch140 (alldevs) to fix #101772. Printing the total size of + all devices was deemed a lower priority than having all devices + (e.g. /dev/ida/c0d9) displayed. + +* Fri Sep 12 2003 Dan Walsh <dwalsh@redhat.com> 2.11y-31 +- turn off selinux + +* Fri Sep 12 2003 Dan Walsh <dwalsh@redhat.com> 2.11y-30.sel +- turn on selinux + +* Fri Sep 5 2003 Elliot Lee <sopwith@redhat.com> 2.11y-28 +- Fix #103004, #103954 + +* Fri Sep 5 2003 Dan Walsh <dwalsh@redhat.com> 2.11y-27 +- turn off selinux + +* Thu Sep 4 2003 Dan Walsh <dwalsh@redhat.com> 2.11y-26.sel +- build with selinux + +* Mon Aug 11 2003 Elliot Lee <sopwith@redhat.com> 2.11y-25 +- Use urandom instead for mkcramfs + +* Tue Jul 29 2003 Dan Walsh <dwalsh@redhat.com> 2.11y-24 +- add SELINUX 2.5 support + +* Wed Jul 23 2003 Elliot Lee <sopwith@redhat.com> 2.11y-22 +- #100433 patch + +* Mon Jun 14 2003 Elliot Lee <sopwith@redhat.com> 2.11y-20 +- #97381 patch + +* Wed Jun 04 2003 Elliot Lee <sopwith@redhat.com> +- rebuilt + +* Mon Apr 21 2003 Elliot Lee <sopwith@redhat.com> 2.11y-17 +- Change patch128 to improve ipcs -l + +* Fri Apr 11 2003 Elliot Lee <sopwith@redhat.com> 2.11y-16 +- Fix #85407 + +* Fri Apr 11 2003 Elliot Lee <sopwith@redhat.com> 2.11y-15 +- Change patch128 to util-linux-2.11f-ipcs-84243-86285.patch to get all +ipcs fixes + +* Thu Apr 10 2003 Matt Wilson <msw@redhat.com> 2.11y-14 +- fix last login date display on AMD64 (#88574) + +* Mon Apr 7 2003 Jeremy Katz <katzj@redhat.com> 2.11y-13 +- include sfdisk on ppc + +* Fri Mar 28 2003 Jeremy Katz <katzj@redhat.com> 2.11y-12 +- add patch from msw to change mkcramfs blocksize with a command line option + +* Tue Mar 25 2003 Phil Knirsch <pknirsch@redhat.com> 2.11y-11 +- Fix segfault on s390x due to wrong usage of BLKGETSIZE. + +* Thu Mar 13 2003 Elliot Lee <sopwith@redhat.com> 2.11y-10 +- Really apply the ipcs patch. Doh. + +* Mon Feb 24 2003 Elliot Lee <sopwith@redhat.com> +- rebuilt + +* Wed Feb 19 2003 Elliot Lee <sopwith@redhat.com> 2.11y-8 +- ipcs-84243.patch to fix #84243 + +* Thu Feb 13 2003 Yukihiro Nakai <ynakai@redhat.com> 2.11y-7 +- Update moremisc patch to fix swprintf()'s minimum field (bug #83361). + +* Mon Feb 03 2003 Elliot Lee <sopwith@redhat.com> 2.11y-6 +- Fix mcookie segfault on many 64-bit architectures (bug #83345). + +* Mon Feb 03 2003 Tim Waugh <twaugh@redhat.com> 2.11y-5 +- Fix underlined multibyte characters (bug #83376). + +* Sun Feb 02 2003 Florian La Roche <Florian.LaRoche@redhat.de> +- rebuild to have again a s390 rpm +- disable some more apps for mainframe + +* Wed Jan 29 2003 Elliot Lee <sopwith@redhat.com> 2.11y-4 +- util-linux-2.11y-umask-82552.patch + +* Wed Jan 22 2003 Tim Powers <timp@redhat.com> +- rebuilt + +* Mon Jan 13 2003 Elliot Lee <sopwith@redhat.com> 2.11y-2 +- Fix #81069, #75421 + +* Mon Jan 13 2003 Elliot Lee <sopwith@redhat.com> 2.11y-1 +- Update to 2.11y +- Fix #80953 +- Update patch0, patch107, patch117, patch120 for 2.11y +- Remove patch60, patch61, patch207, patch211, patch212, patch119, patch121 +- Remove patch122, patch200 + +* Wed Oct 30 2002 Elliot Lee <sopwith@redhat.com> 2.11w-2 +- Remove some crack/unnecessary patches while submitting stuff upstream. +- Build with -D_FILE_OFFSET_BITS=64 + +* Tue Oct 29 2002 Elliot Lee <sopwith@redhat.com> 2.11w-1 +- Update to 2.11w, resolve patch conflicts + +* Tue Oct 08 2002 Phil Knirsch <pknirsch@redhat.com> 2.11r-10hammer.3 +- Extended util-linux-2.11b-s390x patch to work again. + +* Thu Oct 03 2002 Elliot Lee <sopwith@redhat.com> 2.11r-10hammer.2 +- Add patch122 for hwclock on x86_64 + +* Thu Sep 12 2002 Than Ngo <than@redhat.com> 2.11r-10hammer.1 +- Fixed pam config files + +* Wed Sep 11 2002 Bernhard Rosenkraenzer <bero@redhat.com> 2.11r-10hammer +- Port to hammer + +* Fri Aug 30 2002 Elliot Lee <sopwith@redhat.com> 2.11r-10 +- Patch120 (hwclock) to fix #72140 +- Include isosize util + +* Wed Aug 7 2002 Elliot Lee <sopwith@redhat.com> 2.11r-9 +- Patch120 (skipraid2) to fix #70353, because the original patch was +totally useless. + +* Fri Aug 2 2002 Elliot Lee <sopwith@redhat.com> 2.11r-8 +- Patch119 (fdisk-add-primary) from #67898 + +* Wed Jul 24 2002 Elliot Lee <sopwith@redhat.com> 2.11r-7 +- Really add the gptsize patch, instead of what I think the patch says. +(+1) + +* Tue Jul 23 2002 Elliot Lee <sopwith@redhat.com> 2.11r-6 +- Add the sp[n].size part of the patch from #69603 + +* Mon Jul 22 2002 Florian La Roche <Florian.LaRoche@redhat.de> +- adjust mainframe patches + +* Tue Jul 2 2002 Bill Nottingham <notting@redhat.com> 2.11r-4 +- only require usermode if we're shipping kbdrate here + +* Fri Jun 28 2002 Trond Eivind Glomsrod <teg@redhat.com> 2.11r-3 +- Port the large swap patch to new util-linux... the off_t changes + now in main aren't sufficient + +* Thu Jun 27 2002 Elliot Lee <sopwith@redhat.com> 2.11r-2 +- Remove swapondetect (patch301) until it avoids possible false positives. + +* Thu Jun 27 2002 Elliot Lee <sopwith@redhat.com> 2.11r-1 +- Update to 2.11r, wheeee +- Remove unused patches + +* Thu Jun 27 2002 Elliot Lee <sopwith@redhat.com> 2.11n-19 +- Make a note here that this package was the source of the single change +contained in util-linux-2.11f-18 (in 7.2/Alpha), and also contains the +rawman patch from util-linux-2.11f-17.1 (in 2.1AS). +- Package has no runtime deps on slang, so remove the BuildRequires: +slang-devel. + +* Fri Jun 21 2002 Tim Powers <timp@redhat.com> +- automated rebuild + +* Thu Jun 20 2002 Elliot Lee <sopwith@redhat.com> 2.11n-17 +- Fix teg's swapondetect patch to not print out the usage message when +'swapon -a -e' is run. (#66690) (edit existing patch) +- Apply hjl's utmp handling patch (#66950) (patch116) +- Fix fdisk man page notes on IDE disk partition limit (#64013) (patch117) +- Fix mount.8 man page notes on vfat shortname option (#65628) (patch117) +- Fix possible cal overflow with widechars (#67090) (patch117) + +* Tue Jun 11 2002 Trond Eivind Glomsrod <teg@redhat.com> 2.11n-16 +- support large swap partitions +- add '-d' option to autodetect available swap partitions + +* Thu May 23 2002 Tim Powers <timp@redhat.com> +- automated rebuild + +* Wed May 15 2002 Elliot Lee <sopwith@redhat.com> 2.11n-14 +- Remove kbdrate (again). + +* Mon Apr 29 2002 Florian La Roche <Florian.LaRoche@redhat.de> +- adjust mainframe patches to apply to current rpm +- do not include fdisk until it is fixed to work on mainframe + +* Mon Apr 01 2002 Elliot Lee <sopwith@redhat.com> 2.11n-12 +- Don't strip binaries - rpm does it for us. + +* Sun Mar 31 2002 Elliot Lee <sopwith@redhat.com> 2.11n-11 +- Apply patch115 from ejb@ql.org for bug #61868 + +* Wed Mar 27 2002 Elliot Lee <sopwith@redhat.com> 2.11n-10 +- Finish fixing #60675 (ipcrm man page), updated the patch. +- Fix #61203 (patch114 - dumboctal.patch). + +* Tue Mar 12 2002 Elliot Lee <sopwith@redhat.com> 2.11n-9 +- Update ctty3 patch to ignore SIGHUP while dropping controlling terminal + +* Fri Mar 08 2002 Elliot Lee <sopwith@redhat.com> 2.11n-8 +- Update ctty3 patch to drop controlling terminal before forking. + +* Fri Mar 08 2002 Elliot Lee <sopwith@redhat.com> 2.11n-7 + Fix various bugs: +- Add patch110 (skipraid) to properly skip devices that are part of a RAID array. +- Add patch111 (mkfsman) to update the mkfs man page's "SEE ALSO" section. +- remove README.cfdisk +- Include partx +- Fix 54741 and related bugs for good(hah!) with patch113 (ctty3) + +* Wed Mar 06 2002 Elliot Lee <sopwith@redhat.com> 2.11n-6 +- Put kbdrate in, add usermode dep. + +* Tue Feb 26 2002 Elliot Lee <sopwith@redhat.com> 2.11n-5 +- Fix #60363 (tweak raw.8 man page, make rawdevices.8 symlink). + +* Tue Jan 28 2002 Bill Nottingham <notting@redhat.com> 2.11n-4 +- remove kbdrate (fixes kbd conflict) + +* Fri Dec 28 2001 Elliot Lee <sopwith@redhat.com> 2.11n-3 +- Add util-linux-2.11n-ownerumount.patch (#56593) +- Add patch102 (util-linux-2.11n-colrm.patch) to fix #51887 +- Fix #53452 nits. +- Fix #56953 (remove tunelp on s390) +- Fix #56459, and in addition switch to using sed instead of perl. +- Fix #58471 +- Fix #57300 +- Fix #37436 +- Fix #32132 + +* Wed Dec 26 2001 Elliot Lee <sopwith@redhat.com> 2.11n-1 +- Update to 2.11n +- Merge mount/losetup back in. + +* Tue Dec 04 2001 Elliot Lee <sopwith@redhat.com> 2.11f-17 +- Add patch38 (util-linux-2.11f-ctty2.patch) to ignore SIGINT/SIGTERM/SIGQUIT in the parent, so that ^\ won't break things. + +* Fri Nov 09 2001 Elliot Lee <sopwith@redhat.com> 2.11f-16 +- Merge patches 36, 75, 76, and 77 into patch #37, to attempt resolve all the remaining issues with #54741. + +* Wed Oct 24 2001 Florian La Roche <Florian.LaRoche@redhat.de> +- add nologin man-page for s390/s390x + +* Wed Oct 24 2001 Bernhard Rosenkraenzer <bero@redhat.com> 2.11f-14 +- Don't build kbdrate on s390/s390x +- Don't make the pivot_root.8 man page executable(!) + +* Tue Oct 23 2001 Elliot Lee <sopwith@redhat.com> 2.11f-13 +- Patch/idea #76 from HJL, fixes bug #54741 (race condition in login +acquisition of controlling terminal). + +* Thu Oct 11 2001 Bill Nottingham <notting@redhat.com> +- fix permissions problem with vipw & shadow files, again (doh!) + +* Tue Oct 09 2001 Erik Troan <ewt@redhat.com> +- added patch from Olaf Kirch to fix possible pwent structure overwriting + +* Fri Sep 28 2001 Elliot Lee <sopwith@redhat.com> 2.11f-10 +- fdisk patch from arjan + +* Sun Aug 26 2001 Elliot Lee <sopwith@redhat.com> 2.11f-9 +- Don't include cfdisk, since it appears to be an even bigger pile of junk than fdisk? :) + +* Wed Aug 1 2001 Tim Powers <timp@redhat.com> +- don't require usermode + +* Mon Jul 30 2001 Elliot Lee <sopwith@redhat.com> 2.11f-7 +- Incorporate kbdrate back in. + +* Mon Jul 30 2001 Bill Nottingham <notting@redhat.com> +- revert the patch that calls setsid() in login that we had reverted + locally but got integrated upstream (#46223) + +* Tue Jul 24 2001 Florian La Roche <Florian.LaRoche@redhat.de> +- correct s390x patch + +* Mon Jul 23 2001 Elliot Lee <sopwith@redhat.com> +- Add my megapatch (various bugs) +- Include pivot_root (#44828) + +* Thu Jul 12 2001 Bill Nottingham <notting@redhat.com> +- make shadow files 0400, not 0600 + +* Wed Jul 11 2001 Bill Nottingham <notting@redhat.com> +- fix permissions problem with vipw & shadow files + +* Mon Jun 18 2001 Florian La Roche <Florian.LaRoche@redhat.de> +- update to 2.11f, remove any merged patches +- add s390x patches for somewhat larger swap + +* Thu Jun 14 2001 Erik Troan <ewt@redhat.com> +- added --verbose patch to mkcramfs; it's much quieter by default now + +* Tue May 22 2001 Erik Troan <ewt@redhat.com> +- removed warning about starting partitions on cylinder 0 -- swap version2 + makes it unnecessary + +* Wed May 9 2001 Bernhard Rosenkraenzer <bero@redhat.com> 2.11b-2 +- Fix up s390x support + +* Mon May 7 2001 Bernhard Rosenkraenzer <bero@redhat.com> 2.11b-1 +- Fix up login for real (a console session should be the controlling tty) + by reverting to 2.10s code (#36839, #36840, #39237) +- Add man page for agetty (#39287) +- 2.11b, while at it + +* Fri Apr 27 2001 Preston Brown <pbrown@redhat.com> 2.11a-4 +- /sbin/nologin from OpenBSD added. + +* Fri Apr 20 2001 Bernhard Rosenkraenzer <bero@redhat.com> 2.11a-3 +- Fix up login - exiting immediately even if the password is correct + is not exactly a nice feature. +- Make definite plans to kill people who update login without checking + if the new version works ;) + +* Tue Apr 17 2001 Erik Troan <ewt@redhat.com> +- upgraded to 2.11a (kbdrate moved to kbd, among other things) +- turned off ALLOW_VCS_USE +- modified mkcramfs to not use a large number of file descriptors +- include mkfs.bfs + +* Sun Apr 8 2001 Matt Wilson <msw@redhat.com> +- changed Requires: kernel >= 2.2.12-7 to Conflicts: kernel < 2.2.12-7 + (fixes a initscripts -> util-linux -> kernel -> initscripts prereq loop) + +* Tue Mar 20 2001 Matt Wilson <msw@redhat.com> +- patched mkcramfs to use the PAGE_SIZE from asm/page.h instead of hard + coding 4096 (fixes mkcramfs on alpha...) + +* Mon Mar 19 2001 Matt Wilson <msw@redhat.com> +- added mkcramfs (from linux/scripts/mkcramfs) + +* Mon Feb 26 2001 Tim Powers <timp@redhat.com> +- fixed bug #29131, where ipc.info didn't have an info dir entry, + added the dir entry to ipc.texi (Patch58) + +* Fri Feb 23 2001 Preston Brown <pbrown@redhat.com> +- use lang finder script +- install info files + +* Thu Feb 08 2001 Erik Troan <ewt@redhat.com> +- reverted login patch; seems to cause problems +- added agetty + +* Wed Feb 07 2001 Erik Troan <ewt@redhat.com> +- updated kill man page +- added patch to fix vipw race +- updated vipw to edit /etc/shadow and /etc/gshadow, if appropriate +- added patch to disassociate login from tty, session, and pgrp + +* Tue Feb 06 2001 Erik Troan <ewt@redhat.com> +- fixed problem w/ empty extended partitions +- added patch to fix the date in the more man page +- set OPT to pass optimization flags to make rather then RPM_OPT_FLAG +- fixed fdisk -l /Proc/partitions parsing +- updated to 2.10s + +* Tue Jan 23 2001 Preston Brown <pbrown@redhat.com> +- danish translations added + +* Mon Jan 15 2001 Nalin Dahyabhai <nalin@redhat.com> +- fix segfault in login in btmp patch (#24025) + +* Mon Dec 11 2000 Oliver Paukstadt <oliver.paukstadt@millenux.com> +- ported to s390 + +* Wed Nov 01 2000 Florian La Roche <Florian.LaRoche@redhat.de> +- update to 2.10p +- update patch37 to newer fdisk version + +* Mon Oct 9 2000 Jeff Johnson <jbj@redhat.com> +- update to 2.10o +- fdformat: fixed to work with kernel 2.4.0test6 (Marek Wojtowicz) +- login: not installed suid +- getopt: by default install aux files in /usr/share/misc +- update to 2.10n: +- added blockdev.8 +- change to elvtune (andrea) +- fixed overrun in agetty (vii@penguinpowered.com) +- shutdown: prefer umounting by mount point (rgooch) +- fdisk: added plan9 +- fdisk: remove empty links in chain of extended partitions +- hwclock: handle both /dev/rtc and /dev/efirtc (Bill Nottingham) +- script: added -f (flush) option (Ivan Schreter) +- script: added -q (quiet) option (Per Andreas Buer) +- getopt: updated to version 1.1.0 (Frodo Looijaard) +- Czech messages (Jiri Pavlovsky) +- login.1 man page had not /var/spool/mail path (#16998). +- sln.8 man page (but not executable) included (#10601). +- teach fdisk 0xde(Dell), 0xee(EFI GPT), 0xef(EFI FAT) partitions (#17610). + +* Wed Aug 30 2000 Matt Wilson <msw@redhat.com> +- rebuild to cope with glibc locale binary incompatibility, again + +* Mon Aug 14 2000 Jeff Johnson <jbj@redhat.com> +- setfdprm should open with O_WRONLY, not 3. + +* Fri Aug 11 2000 Jeff Johnson <jbj@redhat.com> +- fdformat should open with O_WRONLY, not 3. + +* Fri Jul 21 2000 Nalin Dahyabhai <nalin@redhat.com> +- make 'look' look in /usr/share/dict + +* Fri Jul 21 2000 Bill Nottingham <notting@redhat.com> +- put /usr/local/sbin:/usr/local/bin in root's path + +* Wed Jul 19 2000 Jakub Jelinek <jakub@redhat.com> +- rebuild to cope with glibc locale binary incompatibility + +* Thu Jul 13 2000 Prospector <bugzilla@redhat.com> +- automatic rebuild + +* Mon Jul 10 2000 Bill Nottingham <notting@redhat.com> +- enable hwclock to use /dev/efirtc on ia64 (gettext is fun. :( ) + +* Mon Jul 3 2000 Bill Nottingham <notting@redhat.com> +- move cfdisk to /usr/sbin, it depends on /usr stuff +- add rescuept + +* Fri Jun 23 2000 Bernhard Rosenkraenzer <bero@redhat.com> +- point more at the correct path to vi (for "v"), Bug #10882 + +* Sun Jun 4 2000 Jeff Johnson <jbj@redhat.com> +- FHS packaging changes. + +* Thu Jun 1 2000 Nalin Dahyabhai <nalin@redhat.com> +- modify PAM setup to use system-auth + +* Mon May 1 2000 Bill Nottingham <notting@redhat.com> +- eek, where did login go? (specfile tweaks) + +* Mon Apr 17 2000 Bernhard Rosenkraenzer <bero@redhat.com> +- 2.10k +- fix compilation with current glibc + +* Tue Mar 21 2000 Bernhard Rosenkraenzer <bero@redhat.com> +- 2.10h + +* Tue Mar 7 2000 Jeff Johnson <jbj@redhat.com> +- rebuild for sparc baud rates > 38400. + +* Sat Mar 4 2000 Matt Wilson <msw@redhat.com> +- use snprintf - not sprintf - when doing + sprintf ("%%s\n", _("Some string")) to avoid overflows and + segfaults. + +* Mon Feb 21 2000 Jeff Johnson <jbj@redhat.com> +- raw control file was /dev/raw, now /dev/rawctl. +- raw access files were /dev/raw*, now /dev/raw/raw*. + +* Thu Feb 17 2000 Erik Troan <ewt@redhat.com> +- -v argument to mkswap wasn't working + +* Thu Feb 10 2000 Jakub Jelinek <jakub@redhat.com> +- Recognize 0xfd on Sun disklabels as RAID + +* Tue Feb 8 2000 Bill Nottingham <notting@redhat.com> +- more lives in /bin, and was linked against /usr/lib/libnurses. Bad. + +* Thu Feb 03 2000 Jakub Jelinek <jakub@redhat.com> +- update to 2.10f +- fix issues in the new realpath code, avoid leaking memory + +* Tue Feb 01 2000 Cristian Gafton <gafton@redhat.com> +- rebuild to fix dependencies +- add NFSv3 patches + +* Fri Jan 28 2000 Bill Nottingham <notting@redhat.com> +- don't require csh + +* Mon Jan 24 2000 Nalin Dahyabhai <nalin@redhat.com> +- update to 2.10e +- add rename + +* Thu Jan 20 2000 Jeff Johnson <jbj@redhat.com> +- strip newlines in logger input. + +* Mon Jan 10 2000 Jeff Johnson <jbj@redhat.com> +- rebuild with correct ncurses libs. + +* Tue Dec 7 1999 Matt Wilson <msw@redhat.com> +- updated to util-linux 2.10c +- deprecated IMAP login mail notification patch17 +- deprecated raw patch22 +- depricated readprofile patch24 + +* Tue Dec 7 1999 Bill Nottingham <notting@redhat.com> +- add patch for readprofile + +* Thu Nov 18 1999 Michael K. Johnson <johnsonm@redhat.com> +- tunelp should come from util-linux + +* Tue Nov 9 1999 Jakub Jelinek <jakub@redhat.com> +- kbdrate cannot use /dev/port on sparc. + +* Wed Nov 3 1999 Jakub Jelinek <jakub@redhat.com> +- fix kbdrate on sparc. + +* Wed Oct 27 1999 Bill Nottingham <notting@redhat.com> +- ship hwclock on alpha. + +* Tue Oct 5 1999 Bill Nottingham <notting@redhat.com> +- don't ship symlinks to rdev if we don't ship rdev. + +* Tue Sep 07 1999 Cristian Gafton <gafton@redhat.com> +- add rawIO support from sct + +* Mon Aug 30 1999 Preston Brown <pbrown@redhat.com> +- don't display "new mail" message when the only piece of mail is from IMAP + +* Fri Aug 27 1999 Michael K. Johnson <johnsonm@redhat.com> +- kbdrate is now a console program + +* Thu Aug 26 1999 Jeff Johnson <jbj@redhat.com> +- hostid is now in sh-utils. On sparc, install hostid as sunhostid (#4581). +- update to 2.9w: +- Updated mount.8 (Yann Droneaud) +- Improved makefiles +- Fixed flaw in fdisk + +* Tue Aug 10 1999 Jeff Johnson <jbj@redhat.com> +- tsort is now in textutils. + +* Wed Aug 4 1999 Bill Nottingham <notting@redhat.com> +- turn off setuid bit on login. Again. :( + +* Tue Aug 3 1999 Peter Jones, <pjones@redhat.com> +- hostid script for sparc (#3803). + +* Tue Aug 03 1999 Christian 'Dr. Disk' Hechelmann <drdisk@tc-gruppe.de> +- added locale message catalogs to %%file +- added patch for non-root build +- vigr.8 and /usr/lib/getopt man-page was missing from file list +- /etc/fdprm really is a config file + +* Fri Jul 23 1999 Jeff Johnson <jbj@redhat.com> +- update to 2.9v: +- cfdisk no longer believes the kernel's HDGETGEO + (and may be able to partition a 2 TB disk) + +* Fri Jul 16 1999 Jeff Johnson <jbj@redhat.com> +- update to 2.9u: +- Czech more.help and messages (Jiri Pavlovsky) +- Japanese messages (Daisuke Yamashita) +- fdisk fix (Klaus G. Wagner) +- mount fix (Hirokazu Takahashi) +- agetty: enable hardware flow control (Thorsten Kranzkowski) +- minor cfdisk improvements +- fdisk no longer accepts a default device +- Makefile fix + +* Tue Jul 6 1999 Jeff Johnson <jbj@redhat.com> +- update to 2.9t: +- national language support for hwclock +- Japanese messages (both by Daisuke Yamashita) +- German messages and some misc i18n fixes (Elrond) +- Czech messages (Jiri Pavlovsky) +- wall fixed for /dev/pts/xx ttys +- make last and wall use getutent() (Sascha Schumann) + [Maybe this is bad: last reading all of wtmp may be too slow. + Revert in case people complain.] +- documented UUID= and LABEL= in fstab.5 +- added some partition types +- swapon: warn only if verbose + +* Fri Jun 25 1999 Jeff Johnson <jbj@redhat.com> +- update to 2.9s. + +* Sat May 29 1999 Jeff Johnson <jbj@redhat.com> +- fix mkswap sets incorrect bits on sparc64 (#3140). + +* Thu Apr 15 1999 Jeff Johnson <jbj@redhat.com> +- on sparc64 random ioctls on clock interface cause kernel messages. + +* Thu Apr 15 1999 Jeff Johnson <jbj@redhat.com> +- improved raid patch (H.J. Lu). + +* Wed Apr 14 1999 Michael K. Johnson <johnsonm@redhat.com> +- added patch for smartraid controllers + +* Sat Apr 10 1999 Cristian Gafton <gafton@redhat.com> +- fix logging problems caused by setproctitle and PAM interaction + (#2045) + +* Wed Mar 31 1999 Jeff Johnson <jbj@redhat.com> +- include docs and examples for sfdisk (#1164) + +* Mon Mar 29 1999 Matt Wilson <msw@redhat.com> +- rtc is not working properly on alpha, we can't use hwclock yet. + +* Fri Mar 26 1999 Cristian Gafton <gafton@redhat.com> +- add patch to make mkswap more 64 bit friendly... Patch from + eranian@hpl.hp.com (ahem!) + +* Thu Mar 25 1999 Jeff Johnson <jbj@redhat.com> +- include sfdisk (#1164) +- fix write (#1784) +- use positive logic in spec file (ifarch rather than ifnarch). +- (re)-use 1st matching utmp slot if search by mypid not found. +- update to 2.9o +- lastb wants bad logins in wtmp clone /var/run/btmp (#884) + +* Thu Mar 25 1999 Jakub Jelinek <jj@ultra.linux.cz> +- if hwclock is to be compiled on sparc, + it must actually work. Also, it should obsolete + clock, otherwise it clashes. +- limit the swap size in mkswap for 2.2.1+ kernels + by the actual maximum size kernel can handle. +- fix kbdrate on sparc, patch by J. S. Connell + <ankh@canuck.gen.nz> + +* Wed Mar 24 1999 Matt Wilson <msw@redhat.com> +- added pam_console back into pam.d/login + +* Tue Mar 23 1999 Matt Wilson <msw@redhat.com> +- updated to 2.9i +- added hwclock for sparcs and alpha + +* Mon Mar 22 1999 Erik Troan <ewt@redhat.com> +- added vigr to file list + +* Sun Mar 21 1999 Cristian Gafton <gafton@redhat.com> +- auto rebuild in the new build environment (release 12) + +* Thu Mar 18 1999 Cristian Gafton <gafton@redhat.com> +- remove most of the ifnarch arm stuff + +* Mon Mar 15 1999 Michael Johnson <johnsonm@redhat.com> +- added pam_console.so to /etc/pam.d/login + +* Thu Feb 4 1999 Michael K. Johnson <johnsonm@redhat.com> +- .perms patch to login to make it retain root in parent process + for pam_close_session to work correctly + +* Tue Jan 12 1999 Jeff Johnson <jbj@redhat.com> +- strip fdisk in buildroot correctly (#718) + +* Mon Jan 11 1999 Cristian Gafton <gafton@redhat.com> +- have fdisk compiled on sparc and arm + +* Mon Jan 11 1999 Erik Troan <ewt@redhat.com> +- added beos partition type to fdisk + +* Wed Dec 30 1998 Cristian Gafton <gafton@redhat.com> +- incorporate fdisk on all arches + +* Sat Dec 5 1998 Jeff Johnson <jbj@redhat.com> +- restore PAM functionality at end of login (Bug #201) + +* Thu Dec 03 1998 Cristian Gafton <gafton@redhat.com> +- patch top build on the arm without PAM and related utilities, for now. +- build hwclock only on intel + +* Wed Nov 18 1998 Cristian Gafton <gafton@redhat.com> +- upgraded to version 2.9 + +* Thu Oct 29 1998 Bill Nottingham <notting@redhat.com> +- build for Raw Hide (slang-1.2.2) +- patch kbdrate wackiness so it builds with egcs + +* Tue Oct 13 1998 Erik Troan <ewt@redhat.com> +- patched more to use termcap + +* Mon Oct 12 1998 Erik Troan <ewt@redhat.com> +- added warning about alpha/bsd label starting cylinder + +* Mon Sep 21 1998 Erik Troan <ewt@redhat.com> +- use sigsetjmp/siglongjmp in more rather then sig'less versions + +* Fri Sep 11 1998 Jeff Johnson <jbj@redhat.com> +- explicit attrs for setuid/setgid programs + +* Thu Aug 27 1998 Cristian Gafton <gafton@redhat.com> +- sln is now included in glibc + +* Sun Aug 23 1998 Jeff Johnson <jbj@redhat.com> +- add cbm1581 floppy definitions (problem #787) + +* Mon Jun 29 1998 Jeff Johnson <jbj@redhat.com> +- remove /etc/nologin at end of shutdown/halt. + +* Fri Jun 19 1998 Jeff Johnson <jbj@redhat.com> +- add mount/losetup. + +* Thu Jun 18 1998 Jeff Johnson <jbj@redhat.com> +- update to 2.8 with 2.8b clean up. hostid now defunct? + +* Mon Jun 01 1998 David S. Miller <davem@dm.cobaltmicro.com> +- "more" now works properly on sparc + +* Sat May 02 1998 Jeff Johnson <jbj@redhat.com> +- Fix "fdisk -l" fault on mounted cdrom. (prob #513) + +* Fri Apr 24 1998 Prospector System <bugs@redhat.com> +- translations modified for de, fr, tr + +* Sat Apr 11 1998 Cristian Gafton <gafton@redhat.com> +- manhattan rebuild + +* Mon Dec 29 1997 Erik Troan <ewt@redhat.com> +- more didn't suspend properly on glibc +- use proper tc*() calls rather then ioctl's + +* Sun Dec 21 1997 Cristian Gafton <gafton@redhat.com> +- fixed a security problem in chfn and chsh accepting too + long gecos fields + +* Fri Dec 19 1997 Mike Wangsmo <wanger@redhat.com> +- removed "." from default path + +* Tue Dec 02 1997 Cristian Gafton <gafton@redhat.com> +- added (again) the vipw patch + +* Wed Oct 22 1997 Michael Fulbright <msf@redhat.com> +- minor cleanups for glibc 2.1 + +* Fri Oct 17 1997 Michael Fulbright <msf@redhat.com> +- added vfat32 filesystem type to list recognized by fdisk + +* Fri Oct 10 1997 Erik Troan <ewt@redhat.com> +- don't build clock on the alpha +- don't install chkdupexe + +* Thu Oct 02 1997 Michael K. Johnson <johnsonm@redhat.com> +- Update to new pam standard. +- BuildRoot. + +* Thu Sep 25 1997 Cristian Gafton <gafton@redhat.com> +- added rootok and setproctitle patches +- updated pam config files for chfn and chsh + +* Tue Sep 02 1997 Erik Troan <ewt@redhat.com> +- updated MCONFIG to automatically determine the architecture +- added glibc header hacks to fdisk code +- rdev is only available on the intel + +* Fri Jul 18 1997 Erik Troan <ewt@redhat.com> +- update to util-linux 2.7, fixed login problems + +* Wed Jun 25 1997 Erik Troan <ewt@redhat.com> +- Merged Red Hat changes into main util-linux source, updated package to + development util-linux (nearly 2.7). + +* Tue Apr 22 1997 Michael K. Johnson <johnsonm@redhat.com> +- LOG_AUTH --> LOG_AUTHPRIV in login and shutdown + +* Mon Mar 03 1997 Michael K. Johnson <johnsonm@redhat.com> +- Moved to new pam and from pam.conf to pam.d + +* Tue Feb 25 1997 Michael K. Johnson <johnsonm@redhat.com> +- pam.patch differentiated between different kinds of bad logins. + In particular, "user does not exist" and "bad password" were treated + differently. This was a minor security hole.