summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorManuel Bentele2020-12-02 13:08:58 +0100
committerManuel Bentele2020-12-02 13:08:58 +0100
commit9fc030ed9bffec9f9715595dd5a205a353912d3c (patch)
tree8137142e94abf7cd4eefd3b591c487d034b57e50 /src
parentSetup xloop device with XLOOP_CONFIGURE ioctl call (diff)
downloadxloop-9fc030ed9bffec9f9715595dd5a205a353912d3c.tar.gz
xloop-9fc030ed9bffec9f9715595dd5a205a353912d3c.tar.xz
xloop-9fc030ed9bffec9f9715595dd5a205a353912d3c.zip
Update xlosetup's 'lib' and 'libsmartcol' from util-linux 2.36.1
Diffstat (limited to 'src')
-rw-r--r--src/utils/config.h9
-rw-r--r--src/utils/include/all-io.h38
-rw-r--r--src/utils/include/buffer.h28
-rw-r--r--src/utils/include/fileutils.h4
-rw-r--r--src/utils/include/jsonwrt.h44
-rw-r--r--src/utils/include/loopdev.h1
-rw-r--r--src/utils/include/procutils.h3
-rw-r--r--src/utils/include/pt-gpt-partnames.h14
-rw-r--r--src/utils/include/randutils.h2
-rw-r--r--src/utils/include/strutils.h38
-rw-r--r--src/utils/include/swapheader.h23
-rw-r--r--src/utils/include/swapprober.h9
-rw-r--r--src/utils/lib/CMakeLists.txt2
-rw-r--r--src/utils/lib/buffer.c162
-rw-r--r--src/utils/lib/caputils.c107
-rw-r--r--src/utils/lib/env.c9
-rw-r--r--src/utils/lib/exec_shell.c2
-rw-r--r--src/utils/lib/fileutils.c54
-rw-r--r--src/utils/lib/jsonwrt.c128
-rw-r--r--src/utils/lib/linux_version.c4
-rw-r--r--src/utils/lib/pager.c4
-rw-r--r--src/utils/lib/procutils.c55
-rw-r--r--src/utils/lib/pty-session.c77
-rw-r--r--src/utils/lib/pwdutils.c4
-rw-r--r--src/utils/lib/randutils.c21
-rw-r--r--src/utils/lib/signames.c45
-rw-r--r--src/utils/lib/strutils.c32
-rw-r--r--src/utils/lib/sysfs.c34
-rw-r--r--src/utils/lib/timer.c3
-rw-r--r--src/utils/libsmartcols/CMakeLists.txt1
-rw-r--r--src/utils/libsmartcols/src/fput.c97
-rw-r--r--src/utils/libsmartcols/src/libsmartcols.h9
-rw-r--r--src/utils/libsmartcols/src/print-api.c19
-rw-r--r--src/utils/libsmartcols/src/print.c98
-rw-r--r--src/utils/libsmartcols/src/smartcolsP.h16
35 files changed, 871 insertions, 325 deletions
diff --git a/src/utils/config.h b/src/utils/config.h
index 781ddc7..35ec9d5 100644
--- a/src/utils/config.h
+++ b/src/utils/config.h
@@ -288,6 +288,9 @@
/* Define to 1 if you have the <linux/fd.h> header file. */
#define HAVE_LINUX_FD_H 1
+/* Define to 1 if you have the <linux/fiemap.h> header file. */
+#define HAVE_LINUX_FIEMAP_H 1
+
/* Define to 1 if you have the <linux/fs.h> header file. */
/* #undef HAVE_LINUX_FS_H */
@@ -450,6 +453,9 @@
/* Define to 1 if you have the <security/pam_misc.h> header file. */
#define HAVE_SECURITY_PAM_MISC_H 1
+/* Define to 1 if you have the `sendfile' function. */
+#define HAVE_SENDFILE 1
+
/* Define to 1 if you have the `setitimer' function. */
/* #undef HAVE_SETITIMER */
@@ -576,6 +582,9 @@
/* Define to 1 if you have the <sys/resource.h> header file. */
#define HAVE_SYS_RESOURCE_H 1
+/* Define to 1 if you have the <sys/sendfile.h> header file. */
+#define HAVE_SYS_SENDFILE_H 1
+
/* Define to 1 if you have the <sys/signalfd.h> header file. */
#define HAVE_SYS_SIGNALFD_H 1
diff --git a/src/utils/include/all-io.h b/src/utils/include/all-io.h
index 8ffa9cf..5ed2d11 100644
--- a/src/utils/include/all-io.h
+++ b/src/utils/include/all-io.h
@@ -12,6 +12,10 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SENDFILE_H
+# include <sys/sendfile.h>
+#endif
#include "c.h"
@@ -63,13 +67,15 @@ static inline ssize_t read_all(int fd, char *buf, size_t count)
memset(buf, 0, count);
while (count > 0) {
ret = read(fd, buf, count);
- if (ret <= 0) {
- if (ret < 0 && (errno == EAGAIN || errno == EINTR) && (tries++ < 5)) {
+ if (ret < 0) {
+ if ((errno == EAGAIN || errno == EINTR) && (tries++ < 5)) {
xusleep(250000);
continue;
}
return c ? c : -1;
}
+ if (ret == 0)
+ return c;
tries = 0;
count -= ret;
buf += ret;
@@ -78,4 +84,32 @@ static inline ssize_t read_all(int fd, char *buf, size_t count)
return c;
}
+static inline ssize_t sendfile_all(int out, int in, off_t *off, size_t count)
+{
+#if defined(HAVE_SENDFILE) && defined(__linux__)
+ ssize_t ret;
+ ssize_t c = 0;
+ int tries = 0;
+ while (count) {
+ ret = sendfile(out, in, off, count);
+ if (ret < 0) {
+ if ((errno == EAGAIN || errno == EINTR) && (tries++ < 5)) {
+ xusleep(250000);
+ continue;
+ }
+ return c ? c : -1;
+ }
+ if (ret == 0)
+ return c;
+ tries = 0;
+ count -= ret;
+ c += ret;
+ }
+ return c;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
#endif /* UTIL_LINUX_ALL_IO_H */
diff --git a/src/utils/include/buffer.h b/src/utils/include/buffer.h
new file mode 100644
index 0000000..5bc7037
--- /dev/null
+++ b/src/utils/include/buffer.h
@@ -0,0 +1,28 @@
+#ifndef UTIL_LINUX_BUFFER
+#define UTIL_LINUX_BUFFER
+
+#include "c.h"
+
+struct ul_buffer {
+ char *begin; /* begin of the data */
+ char *end; /* current end of data */
+
+ size_t sz; /* allocated space for data */
+ size_t chunksize;
+};
+
+#define UL_INIT_BUFFER { .begin = NULL }
+
+void ul_buffer_reset_data(struct ul_buffer *buf);
+void ul_buffer_free_data(struct ul_buffer *buf);
+int ul_buffer_is_empty(struct ul_buffer *buf);
+void ul_buffer_set_chunksize(struct ul_buffer *buf, size_t sz);
+void ul_buffer_refer_string(struct ul_buffer *buf, char *str);
+int ul_buffer_alloc_data(struct ul_buffer *buf, size_t sz);
+int ul_buffer_append_data(struct ul_buffer *buf, const char *data, size_t sz);
+int ul_buffer_append_string(struct ul_buffer *buf, const char *str);
+int ul_buffer_append_ntimes(struct ul_buffer *buf, size_t n, const char *str);
+int ul_buffer_set_data(struct ul_buffer *buf, const char *data, size_t sz);
+char *ul_buffer_get_data(struct ul_buffer *buf);
+
+#endif /* UTIL_LINUX_BUFFER */
diff --git a/src/utils/include/fileutils.h b/src/utils/include/fileutils.h
index 479ad15..618bf39 100644
--- a/src/utils/include/fileutils.h
+++ b/src/utils/include/fileutils.h
@@ -74,4 +74,8 @@ static inline struct dirent *xreaddir(DIR *dp)
extern void close_all_fds(const int exclude[], size_t exsz);
+#define UL_COPY_READ_ERROR (-1)
+#define UL_COPY_WRITE_ERROR (-2)
+int ul_copy_file(int from, int to);
+
#endif /* UTIL_LINUX_FILEUTILS */
diff --git a/src/utils/include/jsonwrt.h b/src/utils/include/jsonwrt.h
new file mode 100644
index 0000000..04ef49e
--- /dev/null
+++ b/src/utils/include/jsonwrt.h
@@ -0,0 +1,44 @@
+#ifndef UTIL_LINUX_JSONWRT_H
+#define UTIL_LINUX_JSONWRT_H
+
+enum {
+ UL_JSON_OBJECT,
+ UL_JSON_ARRAY,
+ UL_JSON_VALUE
+};
+
+struct ul_jsonwrt {
+ FILE *out;
+ int indent;
+
+ unsigned int postponed_break :1;
+};
+
+void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent);
+void ul_jsonwrt_indent(struct ul_jsonwrt *fmt);
+void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type);
+void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type, int islast);
+
+#define ul_jsonwrt_root_open(_f) ul_jsonwrt_open(_f, NULL, UL_JSON_OBJECT)
+#define ul_jsonwrt_root_close(_f) ul_jsonwrt_close(_f, UL_JSON_OBJECT, 1)
+
+#define ul_jsonwrt_array_open(_f, _n) ul_jsonwrt_open(_f, _n, UL_JSON_ARRAY)
+#define ul_jsonwrt_array_close(_f, _l) ul_jsonwrt_close(_f, UL_JSON_ARRAY, _l)
+
+#define ul_jsonwrt_object_open(_f, _n) ul_jsonwrt_open(_f, _n, UL_JSON_OBJECT)
+#define ul_jsonwrt_object_close(_f, _l) ul_jsonwrt_close(_f, UL_JSON_OBJECT, _l)
+
+#define ul_jsonwrt_value_open(_f, _n) ul_jsonwrt_open(_f, _n, UL_JSON_VALUE)
+#define ul_jsonwrt_value_close(_f, _l) ul_jsonwrt_close(_f, UL_JSON_VALUE, _l)
+
+
+void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt,
+ const char *name, const char *data, int islast);
+void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt,
+ const char *name, const char *data, int islast);
+void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt,
+ const char *name, uint64_t data, int islast);
+void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt,
+ const char *name, int data, int islast);
+
+#endif /* UTIL_LINUX_JSONWRT_H */
diff --git a/src/utils/include/loopdev.h b/src/utils/include/loopdev.h
index 9fa0688..0184ffe 100644
--- a/src/utils/include/loopdev.h
+++ b/src/utils/include/loopdev.h
@@ -156,6 +156,7 @@ extern int is_loopdev(const char *device);
extern int loopdev_is_autoclear(const char *device);
extern char *loopdev_get_backing_file(const char *device);
+extern int loopdev_has_backing_file(const char *device);
extern int loopdev_is_used(const char *device, const char *filename,
uint64_t offset, uint64_t sizelimit, int flags);
extern char *loopdev_find_by_backing_file(const char *filename,
diff --git a/src/utils/include/procutils.h b/src/utils/include/procutils.h
index 9f8dd76..c9f5bb5 100644
--- a/src/utils/include/procutils.h
+++ b/src/utils/include/procutils.h
@@ -2,6 +2,7 @@
#define UTIL_LINUX_PROCUTILS
#include <dirent.h>
+#include <sys/types.h>
struct proc_tasks {
DIR *dir;
@@ -31,4 +32,6 @@ 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);
+extern int proc_is_procfs(int fd);
+
#endif /* UTIL_LINUX_PROCUTILS */
diff --git a/src/utils/include/pt-gpt-partnames.h b/src/utils/include/pt-gpt-partnames.h
index 604f2c6..6c54a71 100644
--- a/src/utils/include/pt-gpt-partnames.h
+++ b/src/utils/include/pt-gpt-partnames.h
@@ -50,8 +50,8 @@ 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("69DAD710-2CE4-4E3C-B16C-21A1D49ABED3", N_("Linux root (ARM)")),
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")),
@@ -60,11 +60,21 @@ DEF_GUID("A19D880F-05FC-4D3B-A006-743F0F84911E", N_("Linux RAID")),
DEF_GUID("E6D6D379-F507-44C2-A23C-238F2A3DF928", N_("Linux LVM")),
DEF_GUID("4D21B016-B534-45C2-A9FB-5C16E091FD2D", N_("Linux variable data")),
DEF_GUID("7EC6F557-3BC5-4ACA-B293-16EF5DF639D1", N_("Linux temporary data")),
+DEF_GUID("75250D76-8CC6-458E-BD66-BD47CC81A812", N_("Linux /usr (x86)")),
+DEF_GUID("8484680C-9521-48C6-9C11-B0720656F69E", N_("Linux /usr (x86-64)")),
+DEF_GUID("7D0359A3-02B3-4F0A-865C-654403E70625", N_("Linux /usr (ARM)")),
+DEF_GUID("B0E01050-EE5F-4390-949A-9101B17104E9", N_("Linux /usr (ARM-64)")),
+DEF_GUID("4301D2A6-4E3B-4B2A-BB94-9E0B2C4225EA", N_("Linux /usr (IA-64)")),
DEF_GUID("D13C5D3B-B5D1-422A-B29F-9454FDC89D76", N_("Linux root verity (x86)")),
-DEF_GUID("7386CDF2-203C-47A9-A498-F2ECCE45A2D6", N_("Linux root verity (ARM)")),
DEF_GUID("2C7357ED-EBD2-46D9-AEC1-23D437EC2BF5", N_("Linux root verity (x86-64)")),
+DEF_GUID("7386CDF2-203C-47A9-A498-F2ECCE45A2D6", N_("Linux root verity (ARM)")),
DEF_GUID("DF3300CE-D69F-4C92-978C-9BFB0F38D820", N_("Linux root verity (ARM-64)")),
DEF_GUID("86ED10D5-B607-45BB-8957-D350F23D0571", N_("Linux root verity (IA-64)")),
+DEF_GUID("8F461B0D-14EE-4E81-9AA9-049B6FB97ABD", N_("Linux /usr verity (x86)")),
+DEF_GUID("77FF5F63-E7B6-4633-ACF4-1565B864C0E6", N_("Linux /usr verity (x86-64)")),
+DEF_GUID("C215D751-7BCD-4649-BE90-6627490A4C05", N_("Linux /usr verity (ARM)")),
+DEF_GUID("6E11A4E7-FBCA-4DED-B9E9-E1A512BB664E", N_("Linux /usr verity (ARM-64)")),
+DEF_GUID("6A491E03-3BE7-4545-8E38-83320E0EA880", N_("Linux /usr verity (IA-64)")),
/* ... 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")),
diff --git a/src/utils/include/randutils.h b/src/utils/include/randutils.h
index 86e35f3..690bf5e 100644
--- a/src/utils/include/randutils.h
+++ b/src/utils/include/randutils.h
@@ -11,7 +11,7 @@ extern int rand_get_number(int low_n, int high_n);
/* /dev/urandom based with fallback to rand() */
extern int random_get_fd(void);
-extern void random_get_bytes(void *buf, size_t nbytes);
+extern int ul_random_get_bytes(void *buf, size_t nbytes);
extern const char *random_tell_source(void);
#endif
diff --git a/src/utils/include/strutils.h b/src/utils/include/strutils.h
index 4b3182f..09cc35e 100644
--- a/src/utils/include/strutils.h
+++ b/src/utils/include/strutils.h
@@ -9,6 +9,8 @@
#include <stdio.h>
#include <errno.h>
+#include "c.h"
+
/* initialize a custom exit code for all *_or_err functions */
extern void strutils_set_exitcode(int exit_code);
@@ -61,8 +63,13 @@ extern char *strnchr(const char *s, size_t maxlen, int c);
/* caller guarantees n > 0 */
static inline void xstrncpy(char *dest, const char *src, size_t n)
{
- strncpy(dest, src, n-1);
- dest[n-1] = 0;
+ size_t len = src ? strlen(src) : 0;
+
+ if (!len)
+ return;
+ len = min(len, n - 1);
+ memcpy(dest, src, len);
+ dest[len] = 0;
}
/* This is like strncpy(), but based on memcpy(), so compilers and static
@@ -303,6 +310,33 @@ static inline size_t ltrim_whitespace(unsigned char *str)
return len;
}
+/* Removes left-hand, right-hand and repeating whitespaces.
+ */
+static inline size_t normalize_whitespace(unsigned char *str)
+{
+ size_t i, x, sz = strlen((char *) str);
+ int nsp = 0, intext = 0;
+
+ if (!sz)
+ return 0;
+
+ for (i = 0, x = 0; i < sz; ) {
+ if (isspace(str[i]))
+ nsp++;
+ else
+ nsp = 0, intext = 1;
+
+ if (nsp > 1 || (nsp && !intext))
+ i++;
+ else
+ str[x++] = str[i++];
+ }
+ if (nsp) /* tailing space */
+ x--;
+ str[x] = '\0';
+ return x;
+}
+
static inline void strrep(char *s, int find, int replace)
{
while (s && *s && (s = strchr(s, find)) != NULL)
diff --git a/src/utils/include/swapheader.h b/src/utils/include/swapheader.h
deleted file mode 100644
index 3fce0d0..0000000
--- a/src/utils/include/swapheader.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef _SWAPHEADER_H
-#define _SWAPHEADER_H
-
-#define SWAP_VERSION 1
-#define SWAP_UUID_LENGTH 16
-#define SWAP_LABEL_LENGTH 16
-#define SWAP_SIGNATURE "SWAPSPACE2"
-#define SWAP_SIGNATURE_SZ (sizeof(SWAP_SIGNATURE) - 1)
-
-#include <stdint.h>
-
-struct swap_header_v1_2 {
- char bootbits[1024]; /* Space for disklabel etc. */
- uint32_t version;
- uint32_t last_page;
- uint32_t nr_badpages;
- unsigned char uuid[SWAP_UUID_LENGTH];
- char volume_name[SWAP_LABEL_LENGTH];
- uint32_t padding[117];
- uint32_t badpages[1];
-};
-
-#endif /* _SWAPHEADER_H */
diff --git a/src/utils/include/swapprober.h b/src/utils/include/swapprober.h
deleted file mode 100644
index 5107700..0000000
--- a/src/utils/include/swapprober.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef UTIL_LINUX_SWAP_PROBER_H
-#define UTIL_LINUX_SWAP_PROBER_H
-
-#include <blkid.h>
-
-blkid_probe get_swap_prober(const char *devname);
-
-#endif /* UTIL_LINUX_SWAP_PROBER_H */
-
diff --git a/src/utils/lib/CMakeLists.txt b/src/utils/lib/CMakeLists.txt
index ac70039..1a92d32 100644
--- a/src/utils/lib/CMakeLists.txt
+++ b/src/utils/lib/CMakeLists.txt
@@ -5,6 +5,7 @@ project(xloop-utils-lib
LANGUAGES C)
add_library(libcommon STATIC ${CMAKE_CURRENT_SOURCE_DIR}/blkdev.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/buffer.c
${CMAKE_CURRENT_SOURCE_DIR}/canonicalize.c
${CMAKE_CURRENT_SOURCE_DIR}/caputils.c
${CMAKE_CURRENT_SOURCE_DIR}/color-names.c
@@ -18,6 +19,7 @@ add_library(libcommon STATIC ${CMAKE_CURRENT_SOURCE_DIR}/blkdev.c
${CMAKE_CURRENT_SOURCE_DIR}/fileutils.c
${CMAKE_CURRENT_SOURCE_DIR}/idcache.c
${CMAKE_CURRENT_SOURCE_DIR}/ismounted.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/jsonwrt.c
${CMAKE_CURRENT_SOURCE_DIR}/langinfo.c
${CMAKE_CURRENT_SOURCE_DIR}/linux_version.c
${CMAKE_CURRENT_SOURCE_DIR}/loopdev.c
diff --git a/src/utils/lib/buffer.c b/src/utils/lib/buffer.c
new file mode 100644
index 0000000..2658211
--- /dev/null
+++ b/src/utils/lib/buffer.c
@@ -0,0 +1,162 @@
+/*
+ * 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 "buffer.h"
+
+void ul_buffer_reset_data(struct ul_buffer *buf)
+{
+ if (buf->begin)
+ buf->begin[0] = '\0';
+ buf->end = buf->begin;
+}
+
+void ul_buffer_free_data(struct ul_buffer *buf)
+{
+ assert(buf);
+
+ free(buf->begin);
+ buf->begin = NULL;
+ buf->end = NULL;
+ buf->sz = 0;
+}
+
+void ul_buffer_set_chunksize(struct ul_buffer *buf, size_t sz)
+{
+ buf->chunksize = sz;
+}
+
+int ul_buffer_is_empty(struct ul_buffer *buf)
+{
+ return buf->begin == buf->end;
+}
+
+void ul_buffer_refer_string(struct ul_buffer *buf, char *str)
+{
+ if (buf->sz)
+ ul_buffer_free_data(buf);
+ buf->begin = str;
+ buf->sz = str ? strlen(str) : 0;
+ buf->end = buf->begin ? buf->begin + buf->sz : buf->begin;
+}
+
+int ul_buffer_alloc_data(struct ul_buffer *buf, size_t sz)
+{
+ char *tmp;
+ size_t len = 0;
+
+ assert(buf);
+
+ if (sz <= buf->sz)
+ return 0;
+
+ if (buf->end && buf->begin)
+ len = buf->end - buf->begin;
+
+ if (buf->chunksize)
+ sz = ((sz + buf->chunksize) / buf->chunksize) * buf->chunksize + 1;
+
+ tmp = realloc(buf->begin, sz);
+ if (!tmp)
+ return -ENOMEM;
+
+ buf->begin = tmp;
+ buf->end = buf->begin + len;
+ buf->sz = sz;
+
+ return 0;
+}
+
+int ul_buffer_append_data(struct ul_buffer *buf, const char *data, size_t sz)
+{
+ size_t maxsz = 0;
+
+ if (!buf)
+ return -EINVAL;
+ if (!data || !*data)
+ return 0;
+
+ if (buf->begin && buf->end)
+ maxsz = buf->sz - (buf->end - buf->begin);
+
+ if (maxsz <= sz + 1) {
+ int rc = ul_buffer_alloc_data(buf, buf->sz + sz + 1);
+ if (rc)
+ return rc;
+ }
+ if (!buf->end)
+ return -EINVAL; /* make static analyzers happy */
+
+ memcpy(buf->end, data, sz);
+ buf->end += sz;
+ *buf->end = '\0'; /* make sure it's terminated */
+ return 0;
+}
+
+int ul_buffer_append_string(struct ul_buffer *buf, const char *str)
+{
+ return ul_buffer_append_data(buf, str, strlen(str));
+}
+
+int ul_buffer_append_ntimes(struct ul_buffer *buf, size_t n, const char *str)
+{
+ size_t i;
+ size_t len = strlen(str);
+
+ for (i = 0; len && i < n; i++) {
+ int rc = ul_buffer_append_data(buf, str, len);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+int ul_buffer_set_data(struct ul_buffer *buf, const char *data, size_t sz)
+{
+ ul_buffer_reset_data(buf);
+ return ul_buffer_append_data(buf, data, sz);
+}
+
+char *ul_buffer_get_data(struct ul_buffer *buf)
+{
+ return buf->begin;
+}
+
+#ifdef TEST_PROGRAM_BUFFER
+int main(void)
+{
+ struct ul_buffer buf = UL_INIT_BUFFER;
+ char *str;
+
+ ul_buffer_set_chunksize(&buf, 16);
+
+ ul_buffer_append_string(&buf, "AAA");
+ ul_buffer_append_data(&buf, "=", 1);
+ ul_buffer_append_string(&buf, "aaa");
+ ul_buffer_append_data(&buf, ",", 1);
+ ul_buffer_append_string(&buf, "BBB");
+ ul_buffer_append_string(&buf, "=");
+ ul_buffer_append_string(&buf, "bbb");
+
+ str = ul_buffer_get_data(&buf);
+ printf("data '%s'\n", str);
+
+ ul_buffer_reset_data(&buf);
+ ul_buffer_append_string(&buf, "This is really long string to test the buffer function.");
+ ul_buffer_append_string(&buf, " YES!");
+ str = ul_buffer_get_data(&buf);
+ printf("data '%s'\n", str);
+
+ ul_buffer_free_data(&buf);
+ str = strdup("foo");
+ ul_buffer_refer_string(&buf, str);
+ ul_buffer_append_data(&buf, ",", 1);
+ ul_buffer_append_string(&buf, "bar");
+ str = ul_buffer_get_data(&buf);
+ printf("data '%s'\n", str);
+
+ ul_buffer_free_data(&buf);
+}
+#endif /* TEST_PROGRAM_BUFFER */
diff --git a/src/utils/lib/caputils.c b/src/utils/lib/caputils.c
index 17e9c01..13a376b 100644
--- a/src/utils/lib/caputils.c
+++ b/src/utils/lib/caputils.c
@@ -14,32 +14,105 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <sys/prctl.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include "c.h"
#include "caputils.h"
+#include "procutils.h"
#include "pathnames.h"
+static int test_cap(unsigned int cap)
+{
+ /* prctl returns 0 or 1 for valid caps, -1 otherwise */
+ return prctl(PR_CAPBSET_READ, cap, 0, 0, 0) >= 0;
+}
+
+static int cap_last_by_bsearch(int *ret)
+{
+ /* starting with cap=INT_MAX means we always know
+ * that cap1 is invalid after the first iteration */
+ int cap = INT_MAX;
+ unsigned int cap0 = 0, cap1 = INT_MAX;
+
+ while ((int)cap0 < cap) {
+ if (test_cap(cap))
+ cap0 = cap;
+ else
+ cap1 = cap;
+
+ cap = (cap0 + cap1) / 2U;
+ }
+
+ *ret = cap;
+ return 0;
+}
+
+static int cap_last_by_procfs(int *ret)
+{
+ FILE *f = fopen(_PATH_PROC_CAPLASTCAP, "r");
+ int rc = -EINVAL;
+
+ *ret = 0;
+
+ if (f && proc_is_procfs(fileno(f))) {
+ int cap;
+
+ /* we check if the cap after this one really isn't valid */
+ if (fscanf(f, "%d", &cap) == 1 &&
+ cap < INT_MAX && !test_cap(cap + 1)) {
+
+ *ret = cap;
+ rc = 0;
+ }
+ }
+
+ if (f)
+ fclose(f);
+ return rc;
+}
+
int cap_last_cap(void)
{
- /* CAP_LAST_CAP is untrustworthy. */
- static int ret = -1;
- int matched;
- FILE *f;
-
- if (ret != -1)
- return ret;
-
- f = fopen(_PATH_PROC_CAPLASTCAP, "r");
- if (!f) {
- ret = CAP_LAST_CAP; /* guess */
- return ret;
+ static int cap = -1;
+
+ if (cap != -1)
+ return cap;
+ if (cap_last_by_procfs(&cap) < 0)
+ cap_last_by_bsearch(&cap);
+
+ return cap;
+}
+
+#ifdef TEST_PROGRAM_CAPUTILS
+int main(int argc, char *argv[])
+{
+ int rc = 0, cap;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %1$s --last-by-procfs\n"
+ " %1$s --last-by-bsearch\n"
+ " %1$s --last\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
}
- matched = fscanf(f, "%d", &ret);
- fclose(f);
+ if (strcmp(argv[1], "--last-by-procfs") == 0) {
+ rc = cap_last_by_procfs(&cap);
+ if (rc == 0)
+ printf("last cap: %d\n", cap);
+
+ } else if (strcmp(argv[1], "--last-by-bsearch") == 0) {
+ rc = cap_last_by_bsearch(&cap);
+ if (rc == 0)
+ printf("last cap: %d\n", cap);
- if (matched != 1)
- ret = CAP_LAST_CAP; /* guess */
+ } else if (strcmp(argv[1], "--last") == 0)
+ printf("last cap: %d\n", cap_last_cap());
- return ret;
+ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
+#endif
diff --git a/src/utils/lib/env.c b/src/utils/lib/env.c
index c26a5be..ac481ee 100644
--- a/src/utils/lib/env.c
+++ b/src/utils/lib/env.c
@@ -1,10 +1,9 @@
/*
- * Security checks of environment
- * Added from shadow-utils package
- * by Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
+ * environ[] array cleanup code and getenv() wrappers
*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
*/
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -57,7 +56,7 @@ struct ul_env_list {
};
/*
- * Saves @name env.varable to @ls, returns pointer to the new head of the list.
+ * Saves @name env.variable to @ls, returns pointer to the new head of the list.
*/
static struct ul_env_list *env_list_add(struct ul_env_list *ls0, const char *str)
{
diff --git a/src/utils/lib/exec_shell.c b/src/utils/lib/exec_shell.c
index 18798eb..6fef6c7 100644
--- a/src/utils/lib/exec_shell.c
+++ b/src/utils/lib/exec_shell.c
@@ -46,6 +46,6 @@ void __attribute__((__noreturn__)) exec_shell(void)
arg0[0] = '-';
strcpy(arg0 + 1, shell_basename);
- execl(shell, arg0, NULL);
+ execl(shell, arg0, (char *)NULL);
errexec(shell);
}
diff --git a/src/utils/lib/fileutils.c b/src/utils/lib/fileutils.c
index 3ca43c1..9da906a 100644
--- a/src/utils/lib/fileutils.c
+++ b/src/utils/lib/fileutils.c
@@ -1,15 +1,20 @@
/*
+ * This code is in the public domain; do with it what you wish.
+ *
* Copyright (C) 2012 Sami Kerola <kerolasa@iki.fi>
+ * Copyright (C) 2012-2020 Karel Zak <kzak@redhat.com>
*/
-
#include <stdio.h>
#include <stdlib.h>
+#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
+#include <string.h>
#include "c.h"
+#include "all-io.h"
#include "fileutils.h"
#include "pathnames.h"
@@ -172,7 +177,7 @@ void close_all_fds(const int exclude[], size_t exsz)
int main(int argc, char *argv[])
{
if (argc < 2)
- errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds}", argv[0]);
+ errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds,copy-file}", argv[0]);
if (strcmp(argv[1], "--mkstemp") == 0) {
FILE *f;
@@ -192,6 +197,12 @@ int main(int argc, char *argv[])
ignore_result( dup(STDIN_FILENO) );
close_all_fds(wanted_fds, ARRAY_SIZE(wanted_fds));
+ } else if (strcmp(argv[1], "--copy-file") == 0) {
+ int ret = ul_copy_file(STDIN_FILENO, STDOUT_FILENO);
+ if (ret == UL_COPY_READ_ERROR)
+ err(EXIT_FAILURE, "read");
+ else if (ret == UL_COPY_WRITE_ERROR)
+ err(EXIT_FAILURE, "write");
}
return EXIT_SUCCESS;
}
@@ -244,3 +255,42 @@ char *stripoff_last_component(char *path)
*p = '\0';
return p + 1;
}
+
+static int copy_file_simple(int from, int to)
+{
+ ssize_t nr;
+ char buf[BUFSIZ];
+
+ while ((nr = read_all(from, buf, sizeof(buf))) > 0)
+ if (write_all(to, buf, nr) == -1)
+ return UL_COPY_WRITE_ERROR;
+ if (nr < 0)
+ return UL_COPY_READ_ERROR;
+#ifdef HAVE_EXPLICIT_BZERO
+ explicit_bzero(buf, sizeof(buf));
+#endif
+ return 0;
+}
+
+/* Copies the contents of a file. Returns -1 on read error, -2 on write error. */
+int ul_copy_file(int from, int to)
+{
+#ifdef HAVE_SENDFILE
+ struct stat st;
+ ssize_t nw;
+
+ if (fstat(from, &st) == -1)
+ return UL_COPY_READ_ERROR;
+ if (!S_ISREG(st.st_mode))
+ return copy_file_simple(from, to);
+ if (sendfile_all(to, from, NULL, st.st_size) < 0)
+ return copy_file_simple(from, to);
+ /* ensure we either get an EOF or an error */
+ while ((nw = sendfile_all(to, from, NULL, 16*1024*1024)) != 0)
+ if (nw < 0)
+ return copy_file_simple(from, to);
+ return 0;
+#else
+ return copy_file_simple(from, to);
+#endif
+}
diff --git a/src/utils/lib/jsonwrt.c b/src/utils/lib/jsonwrt.c
new file mode 100644
index 0000000..00e8b9d
--- /dev/null
+++ b/src/utils/lib/jsonwrt.c
@@ -0,0 +1,128 @@
+/*
+ * JSON output formatting functions.
+ *
+ * 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 <inttypes.h>
+
+#include "c.h"
+#include "carefulputc.h"
+#include "jsonwrt.h"
+
+
+void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent)
+{
+ fmt->out = out;
+ fmt->indent = indent;
+}
+
+void ul_jsonwrt_indent(struct ul_jsonwrt *fmt)
+{
+ int i;
+
+ for (i = 0; i < fmt->indent; i++)
+ fputs(" ", fmt->out);
+}
+
+void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type)
+{
+ if (fmt->postponed_break && !name)
+ ;
+ else {
+ ul_jsonwrt_indent(fmt);
+ if (name)
+ fputs_quoted_json_lower(name, fmt->out);
+ }
+
+ switch (type) {
+ case UL_JSON_OBJECT:
+ fputs(name ? ": {\n" : "{\n", fmt->out);
+ fmt->indent++;
+ break;
+ case UL_JSON_ARRAY:
+ fputs(name ? ": [\n" : "{\n", fmt->out);
+ fmt->indent++;
+ break;
+ case UL_JSON_VALUE:
+ fputs(name ? ": " : " ", fmt->out);
+ break;
+ }
+ fmt->postponed_break = 0;
+}
+
+void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type, int islast)
+{
+ if (fmt->indent == 0) {
+ fputs("}\n", fmt->out);
+ fmt->indent--;
+ return;
+ }
+ assert(fmt->indent > 0);
+
+ switch (type) {
+ case UL_JSON_OBJECT:
+ fmt->indent--;
+ ul_jsonwrt_indent(fmt);
+ fputs(islast ? "}" : "},", fmt->out);
+ break;
+ case UL_JSON_ARRAY:
+ fmt->indent--;
+ ul_jsonwrt_indent(fmt);
+ fputs(islast ? "]" : "],", fmt->out);
+ break;
+ case UL_JSON_VALUE:
+ if (!islast)
+ fputc(',', fmt->out);
+ break;
+ }
+
+ if (!islast && (type == UL_JSON_OBJECT || type == UL_JSON_ARRAY))
+ fmt->postponed_break = 1;
+ else {
+ fputc('\n', fmt->out);
+ fmt->postponed_break = 0;
+ }
+}
+
+void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt,
+ const char *name, const char *data, int islast)
+{
+ ul_jsonwrt_value_open(fmt, name);
+ if (data && *data)
+ fputs(data, fmt->out);
+ else
+ fputs("null", fmt->out);
+ ul_jsonwrt_value_close(fmt, islast);
+}
+
+void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt,
+ const char *name, const char *data, int islast)
+{
+ ul_jsonwrt_value_open(fmt, name);
+ if (data && *data)
+ fputs_quoted_json(data, fmt->out);
+ else
+ fputs("null", fmt->out);
+ ul_jsonwrt_value_close(fmt, islast);
+}
+
+void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt,
+ const char *name, uint64_t data, int islast)
+{
+ ul_jsonwrt_value_open(fmt, name);
+ fprintf(fmt->out, "%"PRIu64, data);
+ ul_jsonwrt_value_close(fmt, islast);
+}
+
+void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt,
+ const char *name, int data, int islast)
+{
+ ul_jsonwrt_value_open(fmt, name);
+ fputs(data ? "true" : "false", fmt->out);
+ ul_jsonwrt_value_close(fmt, islast);
+}
+
diff --git a/src/utils/lib/linux_version.c b/src/utils/lib/linux_version.c
index 137bbe7..119869e 100644
--- a/src/utils/lib/linux_version.c
+++ b/src/utils/lib/linux_version.c
@@ -1,3 +1,7 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ */
#include <stdio.h>
#include <sys/utsname.h>
diff --git a/src/utils/lib/pager.c b/src/utils/lib/pager.c
index b3cf6ee..747521e 100644
--- a/src/utils/lib/pager.c
+++ b/src/utils/lib/pager.c
@@ -88,14 +88,14 @@ static int start_command(struct child_process *cmd)
if (cmd->pid < 0) {
if (need_in)
close_pair(fdin);
- else if (cmd->in)
+ else if (0 <= cmd->in)
close(cmd->in);
return -1;
}
if (need_in)
close(fdin[0]);
- else if (cmd->in)
+ else if (0 <= cmd->in)
close(cmd->in);
return 0;
}
diff --git a/src/utils/lib/procutils.c b/src/utils/lib/procutils.c
index 8fb5d5c..bf689ab 100644
--- a/src/utils/lib/procutils.c
+++ b/src/utils/lib/procutils.c
@@ -1,17 +1,11 @@
/*
* Copyright (C) 2011 Davidlohr Bueso <dave@gnu.org>
+ * Copyright (C) 2011-2020 Karel Zak <kzak@redhat.com>
*
* procutils.c: General purpose procfs parsing utilities
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library Public License as published by
- * the Free Software Foundation; either version 2, 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 Library Public License for more details.
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
*/
#include <stdio.h>
@@ -19,11 +13,13 @@
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
+#include <sys/vfs.h>
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>
#include "procutils.h"
+#include "statfs_magic.h"
#include "fileutils.h"
#include "all-io.h"
#include "c.h"
@@ -241,6 +237,27 @@ int proc_next_pid(struct proc_processes *ps, pid_t *pid)
return 0;
}
+/* checks if fd is file in a procfs;
+ * returns 1 if true, 0 if false or couldn't determine */
+int proc_is_procfs(int fd)
+{
+ struct statfs st;
+ int ret;
+
+ do {
+ errno = 0;
+ ret = fstatfs(fd, &st);
+
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ return 0;
+ xusleep(250000);
+ }
+ } while (ret != 0);
+
+ return st.f_type == STATFS_PROC_MAGIC;
+}
+
#ifdef TEST_PROGRAM_PROCUTILS
static int test_tasks(int argc, char *argv[])
@@ -289,10 +306,28 @@ static int test_processes(int argc, char *argv[])
return EXIT_SUCCESS;
}
+static int test_isprocfs(int argc, char *argv[])
+{
+ const char *name = argc > 1 ? argv[1] : "/proc";
+ int fd = open(name, O_RDONLY);
+ int is = 0;
+
+ if (fd >= 0) {
+ is = proc_is_procfs(fd);
+ close(fd);
+ } else
+ err(EXIT_FAILURE, "cannot open %s", name);
+
+ printf("%s: %s procfs\n", name, is ? "is" : "is NOT");
+ return is ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+
int main(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "usage: %1$s --tasks <pid>\n"
+ " %1$s --is-procfs [<dir>]\n"
" %1$s --processes [---name <name>] [--uid <uid>]\n",
program_invocation_short_name);
return EXIT_FAILURE;
@@ -302,6 +337,8 @@ int main(int argc, char *argv[])
return test_tasks(argc - 1, argv + 1);
if (strcmp(argv[1], "--processes") == 0)
return test_processes(argc - 1, argv + 1);
+ if (strcmp(argv[1], "--is-procfs") == 0)
+ return test_isprocfs(argc - 1, argv + 1);
return EXIT_FAILURE;
}
diff --git a/src/utils/lib/pty-session.c b/src/utils/lib/pty-session.c
index 06b2a49..f4bb004 100644
--- a/src/utils/lib/pty-session.c
+++ b/src/utils/lib/pty-session.c
@@ -340,12 +340,17 @@ static int handle_io(struct ul_pty *pty, int fd, int *eof)
char buf[BUFSIZ];
ssize_t bytes;
int rc = 0;
+ sigset_t set;
DBG(IO, ul_debugobj(pty, " handle I/O on fd=%d", fd));
*eof = 0;
+ sigemptyset(&set);
+ sigaddset(&set, SIGTTIN);
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
/* read from active FD */
bytes = read(fd, buf, sizeof(buf));
+ sigprocmask(SIG_BLOCK, &set, NULL);
if (bytes < 0) {
if (errno == EAGAIN || errno == EINTR)
return 0;
@@ -409,8 +414,8 @@ void ul_pty_wait_for_child(struct ul_pty *pty)
}
} else {
/* final wait */
- while ((pid = wait3(&status, options, NULL)) > 0) {
- DBG(SIG, ul_debug(" wait3 done [rc=%d]", (int) pid));
+ while ((pid = waitpid(-1, &status, options)) > 0) {
+ DBG(SIG, ul_debug(" waitpid done [rc=%d]", (int) pid));
if (pid == pty->child) {
if (pty->callbacks.child_die)
pty->callbacks.child_die(
@@ -451,9 +456,10 @@ static int handle_signal(struct ul_pty *pty, int fd)
else
ul_pty_wait_for_child(pty);
- } else if (info.ssi_status == SIGSTOP && pty->child > 0)
+ } else if (info.ssi_status == SIGSTOP && pty->child > 0) {
pty->callbacks.child_sigstop(pty->callback_data,
pty->child);
+ }
if (pty->child <= 0) {
DBG(SIG, ul_debugobj(pty, " no child, setting leaving timeout"));
@@ -571,16 +577,15 @@ int ul_pty_proxy_master(struct ul_pty *pty)
rc = mainloop_callback(pty);
if (rc == 0)
continue;
- } else
+ } else {
rc = 0;
+ }
DBG(IO, ul_debugobj(pty, "leaving poll() loop [timeout=%d, rc=%d]", timeout, rc));
break;
}
/* event */
for (i = 0; i < ARRAY_SIZE(pfd); i++) {
- rc = 0;
-
if (pfd[i].revents == 0)
continue;
@@ -594,34 +599,42 @@ int ul_pty_proxy_master(struct ul_pty *pty)
pfd[i].revents & POLLERR ? "POLLERR" : "",
pfd[i].revents & POLLNVAL ? "POLLNVAL" : ""));
- switch (i) {
- case POLLFD_STDIN:
- case POLLFD_MASTER:
- /* data */
- if (pfd[i].revents & POLLIN)
- rc = handle_io(pty, pfd[i].fd, &eof);
- /* EOF maybe detected in two ways; they are as follows:
- * A) poll() return POLLHUP event after close()
- * B) read() returns 0 (no data)
- *
- * POLLNVAL means that fd is closed.
- */
- if ((pfd[i].revents & POLLHUP) || (pfd[i].revents & POLLNVAL) || eof) {
- DBG(IO, ul_debugobj(pty, " ignore FD"));
- pfd[i].fd = -1;
- if (i == POLLFD_STDIN) {
- ul_pty_write_eof_to_child(pty);
- DBG(IO, ul_debugobj(pty, " ignore STDIN"));
- }
- }
- continue;
- case POLLFD_SIGNAL:
+ if (i == POLLFD_SIGNAL)
rc = handle_signal(pty, pfd[i].fd);
+ else if (pfd[i].revents & POLLIN)
+ rc = handle_io(pty, pfd[i].fd, &eof); /* data */
+
+ if (rc) {
+ ul_pty_write_eof_to_child(pty);
break;
}
- if (rc)
- break;
+
+ if (i == POLLFD_SIGNAL)
+ continue;
+
+ /* EOF maybe detected in two ways; they are as follows:
+ * A) poll() return POLLHUP event after close()
+ * B) read() returns 0 (no data)
+ *
+ * POLLNVAL means that fd is closed.
+ */
+ if ((pfd[i].revents & POLLHUP) || (pfd[i].revents & POLLNVAL) || eof) {
+ DBG(IO, ul_debugobj(pty, " ignore FD"));
+ pfd[i].fd = -1;
+ if (i == POLLFD_STDIN) {
+ ul_pty_write_eof_to_child(pty);
+ DBG(IO, ul_debugobj(pty, " ignore STDIN"));
+ }
+ }
}
+ if (rc)
+ break;
+ }
+
+ if (rc && pty->child && pty->child != (pid_t) -1 && !pty->delivered_signal) {
+ kill(pty->child, SIGTERM);
+ sleep(2);
+ kill(pty->child, SIGKILL);
}
pty_signals_cleanup(pty);
@@ -686,9 +699,9 @@ int main(int argc, char *argv[])
shname = shname ? shname + 1 : shell;
if (command)
- execl(shell, shname, "-c", command, NULL);
+ execl(shell, shname, "-c", command, (char *)NULL);
else
- execl(shell, shname, "-i", NULL);
+ execl(shell, shname, "-i", (char *)NULL);
err(EXIT_FAILURE, "failed to execute %s", shell);
break;
diff --git a/src/utils/lib/pwdutils.c b/src/utils/lib/pwdutils.c
index d5f4d2e..d97020c 100644
--- a/src/utils/lib/pwdutils.c
+++ b/src/utils/lib/pwdutils.c
@@ -1,3 +1,7 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ */
#include <stdlib.h>
#include "c.h"
diff --git a/src/utils/lib/randutils.c b/src/utils/lib/randutils.c
index bd2a8f6..39edf4e 100644
--- a/src/utils/lib/randutils.c
+++ b/src/utils/lib/randutils.c
@@ -13,9 +13,9 @@
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
-
+#ifdef __linux__
#include <sys/syscall.h>
-
+#endif
#include "c.h"
#include "randutils.h"
#include "nls.h"
@@ -36,7 +36,7 @@
#endif
#if !defined(HAVE_GETRANDOM) && defined(SYS_getrandom)
-/* libc without function, but we have syscal */
+/* libc without function, but we have syscall */
#define GRND_NONBLOCK 0x01
#define GRND_RANDOM 0x02
static int getrandom(void *buf, size_t buflen, unsigned int flags)
@@ -102,7 +102,12 @@ int random_get_fd(void)
#define UL_RAND_READ_ATTEMPTS 8
#define UL_RAND_READ_DELAY 125000 /* microseconds */
-void random_get_bytes(void *buf, size_t nbytes)
+/*
+ * Write @nbytes random bytes into @buf.
+ *
+ * Returns 0 for good quality of random bytes or 1 for weak quality.
+ */
+int ul_random_get_bytes(void *buf, size_t nbytes)
{
unsigned char *cp = (unsigned char *)buf;
size_t i, n = nbytes;
@@ -118,7 +123,7 @@ void random_get_bytes(void *buf, size_t nbytes)
n -= x;
cp += x;
lose_counter = 0;
-
+ errno = 0;
} else if (errno == ENOSYS) { /* kernel without getrandom() */
break;
@@ -177,6 +182,8 @@ void random_get_bytes(void *buf, size_t nbytes)
sizeof(ul_jrand_seed)-sizeof(unsigned short));
}
#endif
+
+ return n != 0;
}
@@ -216,7 +223,7 @@ int main(int argc, char *argv[])
printf("Multiple random calls:\n");
for (i = 0; i < n; i++) {
- random_get_bytes(&v, sizeof(v));
+ ul_random_get_bytes(&v, sizeof(v));
printf("#%02zu: %25"PRIu64"\n", i, v);
}
@@ -227,7 +234,7 @@ int main(int argc, char *argv[])
if (!buf)
err(EXIT_FAILURE, "failed to allocate buffer");
- random_get_bytes(buf, bufsz);
+ ul_random_get_bytes(buf, bufsz);
for (i = 0; i < n; i++) {
vp = (int64_t *) (buf + (i * sizeof(*vp)));
printf("#%02zu: %25"PRIu64"\n", i, *vp);
diff --git a/src/utils/lib/signames.c b/src/utils/lib/signames.c
index 316eec5..064776a 100644
--- a/src/utils/lib/signames.c
+++ b/src/utils/lib/signames.c
@@ -1,42 +1,11 @@
/*
- * Copyright (c) 1988, 1993, 1994, 2017
- * 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.
- */
-/*
- * 2017-10-14 Niklas Hambüchen <mail@nh2.me>
- * - Extracted signal names mapping from kill.c
- *
- * Copyright (C) 2014 Sami Kerola <kerolasa@iki.fi>
- * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
- * Copyright (C) 2017 Niklas Hambüchen <mail@nh2.me>
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+
+ * Written by:
+ * Sami Kerola <kerolasa@iki.fi>
+ * Karel Zak <kzak@redhat.com>
+ * Niklas Hambüchen <mail@nh2.me>
*/
#include <ctype.h> /* for isdigit() */
diff --git a/src/utils/lib/strutils.c b/src/utils/lib/strutils.c
index 304f314..eec4cd5 100644
--- a/src/utils/lib/strutils.c
+++ b/src/utils/lib/strutils.c
@@ -1114,6 +1114,23 @@ static int test_strutils_cmp_paths(int argc, char *argv[])
return EXIT_SUCCESS;
}
+static int test_strutils_normalize(int argc, char *argv[])
+{
+ unsigned char *str;
+ size_t sz;
+
+ if (argc < 2)
+ return EXIT_FAILURE;
+
+ str = (unsigned char *) strdup(argv[1]);
+ sz = normalize_whitespace(str);
+
+ printf("'%s' --> '%s' [sz=%zu]\n", argv[1], str, sz);
+ free(str);
+
+ return EXIT_SUCCESS;
+}
+
int main(int argc, char *argv[])
{
if (argc == 3 && strcmp(argv[1], "--size") == 0)
@@ -1125,10 +1142,17 @@ int main(int argc, char *argv[])
if (argc == 4 && strcmp(argv[1], "--strdup-member") == 0)
return test_strdup_to_member(argc - 1, argv + 1);
- fprintf(stderr, "usage: %1$s --size <number>[suffix]\n"
- " %1$s --cmp-paths <path> <path>\n"
- " %1$s --strdup-member <str> <str>\n",
- argv[0]);
+ else if (argc == 3 && strcmp(argv[1], "--normalize") == 0)
+ return test_strutils_normalize(argc - 1, argv + 1);
+
+ else {
+ fprintf(stderr, "usage: %1$s --size <number>[suffix]\n"
+ " %1$s --cmp-paths <path> <path>\n"
+ " %1$s --strdup-member <str> <str>\n"
+ " %1$s --normalize <str>\n",
+ argv[0]);
+ exit(EXIT_FAILURE);
+ }
return EXIT_FAILURE;
}
diff --git a/src/utils/lib/sysfs.c b/src/utils/lib/sysfs.c
index 5b4de2c..3b75a23 100644
--- a/src/utils/lib/sysfs.c
+++ b/src/utils/lib/sysfs.c
@@ -874,7 +874,7 @@ int sysfs_devname_is_hidden(const char *prefix, const char *name)
dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char *parent)
{
char buf[PATH_MAX];
- char *_name = NULL; /* name as encoded in sysfs */
+ char *_name = NULL, *_parent = NULL; /* name as encoded in sysfs */
dev_t dev = 0;
int len;
@@ -901,21 +901,20 @@ dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char
goto done;
sysfs_devname_dev_to_sys(_name);
+ if (parent) {
+ _parent = strdup(parent);
+ if (!_parent)
+ goto done;
+ }
+
if (parent && strncmp("dm-", name, 3) != 0) {
/*
* Create path to /sys/block/<parent>/<name>/dev
*/
- char *_parent = strdup(parent);
-
- if (!_parent) {
- free(_parent);
- goto done;
- }
sysfs_devname_dev_to_sys(_parent);
len = snprintf(buf, sizeof(buf),
"%s" _PATH_SYS_BLOCK "/%s/%s/dev",
prefix, _parent, _name);
- free(_parent);
if (len < 0 || (size_t) len >= sizeof(buf))
goto done;
@@ -934,10 +933,22 @@ dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char
goto done;
dev = read_devno(buf);
+ /*
+ * Read from /sys/block/<parent>/<partition>/dev
+ */
+ if (!dev && parent && startswith(name, parent)) {
+ len = snprintf(buf, sizeof(buf),
+ "%s" _PATH_SYS_BLOCK "/%s/%s/dev",
+ prefix, _parent, _name);
+ if (len < 0 || (size_t) len >= sizeof(buf))
+ goto done;
+ dev = read_devno(buf);
+ }
+
+ /*
+ * Read from /sys/block/<sysname>/device/dev
+ */
if (!dev) {
- /*
- * Read from /sys/block/<sysname>/device/dev
- */
len = snprintf(buf, sizeof(buf),
"%s" _PATH_SYS_BLOCK "/%s/device/dev",
prefix, _name);
@@ -947,6 +958,7 @@ dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char
}
done:
free(_name);
+ free(_parent);
return dev;
}
diff --git a/src/utils/lib/timer.c b/src/utils/lib/timer.c
index c1ea54e..cfa18f6 100644
--- a/src/utils/lib/timer.c
+++ b/src/utils/lib/timer.c
@@ -1,4 +1,7 @@
/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
* Please, don't add this file to libcommon because timers requires
* -lrt on systems with old libc (and probably also -lpthread for static
* build).
diff --git a/src/utils/libsmartcols/CMakeLists.txt b/src/utils/libsmartcols/CMakeLists.txt
index 307f06d..7265bea 100644
--- a/src/utils/libsmartcols/CMakeLists.txt
+++ b/src/utils/libsmartcols/CMakeLists.txt
@@ -8,7 +8,6 @@ add_library(libsmartcols STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/buffer.c
${CMAKE_CURRENT_SOURCE_DIR}/src/calculate.c
${CMAKE_CURRENT_SOURCE_DIR}/src/cell.c
${CMAKE_CURRENT_SOURCE_DIR}/src/column.c
- ${CMAKE_CURRENT_SOURCE_DIR}/src/fput.c
${CMAKE_CURRENT_SOURCE_DIR}/src/grouping.c
${CMAKE_CURRENT_SOURCE_DIR}/src/init.c
${CMAKE_CURRENT_SOURCE_DIR}/src/iter.c
diff --git a/src/utils/libsmartcols/src/fput.c b/src/utils/libsmartcols/src/fput.c
deleted file mode 100644
index b00c3d8..0000000
--- a/src/utils/libsmartcols/src/fput.c
+++ /dev/null
@@ -1,97 +0,0 @@
-#include "carefulputc.h"
-#include "smartcolsP.h"
-
-void fput_indent(struct libscols_table *tb)
-{
- int i;
-
- for (i = 0; i <= tb->indent; i++)
- fputs(" ", tb->out);
-}
-
-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;
- }
-}
-
-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;
- }
-}
-
-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++;
-}
-
-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;
- }
-}
-
-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++;
-}
-
-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;
-}
diff --git a/src/utils/libsmartcols/src/libsmartcols.h b/src/utils/libsmartcols/src/libsmartcols.h
index 5714bfa..35c24c9 100644
--- a/src/utils/libsmartcols/src/libsmartcols.h
+++ b/src/utils/libsmartcols/src/libsmartcols.h
@@ -18,6 +18,15 @@ extern "C" {
#include <stdio.h>
#include <sys/types.h>
+#include <xloop/version.h>
+
+/**
+ * LIBSMARTCOLS_VERSION:
+ *
+ * Library version string
+ */
+#define LIBSMARTCOLS_VERSION XLOOP_VERSION
+
/**
* libscols_iter:
*
diff --git a/src/utils/libsmartcols/src/print-api.c b/src/utils/libsmartcols/src/print-api.c
index 9a9f2df..89dd3bf 100644
--- a/src/utils/libsmartcols/src/print-api.c
+++ b/src/utils/libsmartcols/src/print-api.c
@@ -117,8 +117,11 @@ static int do_print_table(struct libscols_table *tb, int *is_empty)
if (list_empty(&tb->tb_lines)) {
DBG(TAB, ul_debugobj(tb, "ignore -- no lines"));
if (scols_table_is_json(tb)) {
- fput_table_open(tb);
- fput_table_close(tb);
+ ul_jsonwrt_init(&tb->json, tb->out, 0);
+ ul_jsonwrt_root_open(&tb->json);
+ ul_jsonwrt_array_open(&tb->json, tb->name);
+ ul_jsonwrt_array_close(&tb->json, 1);
+ ul_jsonwrt_root_close(&tb->json);
} else if (is_empty)
*is_empty = 1;
return 0;
@@ -129,7 +132,10 @@ static int do_print_table(struct libscols_table *tb, int *is_empty)
if (rc)
return rc;
- fput_table_open(tb);
+ if (scols_table_is_json(tb)) {
+ ul_jsonwrt_root_open(&tb->json);
+ ul_jsonwrt_array_open(&tb->json, tb->name);
+ }
if (tb->format == SCOLS_FMT_HUMAN)
__scols_print_title(tb);
@@ -143,7 +149,10 @@ static int do_print_table(struct libscols_table *tb, int *is_empty)
else
rc = __scols_print_table(tb, buf);
- fput_table_close(tb);
+ if (scols_table_is_json(tb)) {
+ ul_jsonwrt_array_close(&tb->json, 1);
+ ul_jsonwrt_root_close(&tb->json);
+ }
done:
__scols_cleanup_printing(tb, buf);
return rc;
@@ -162,7 +171,7 @@ int scols_print_table(struct libscols_table *tb)
int empty = 0;
int rc = do_print_table(tb, &empty);
- if (rc == 0 && !empty)
+ if (rc == 0 && !empty && !scols_table_is_json(tb))
fputc('\n', tb->out);
return rc;
}
diff --git a/src/utils/libsmartcols/src/print.c b/src/utils/libsmartcols/src/print.c
index 1172533..9d78c90 100644
--- a/src/utils/libsmartcols/src/print.c
+++ b/src/utils/libsmartcols/src/print.c
@@ -444,6 +444,7 @@ static int print_data(struct libscols_table *tb,
size_t len = 0, i, width, bytes;
const char *color = NULL;
char *data, *nextchunk;
+ const char *name = NULL;
int is_last;
assert(tb);
@@ -453,8 +454,16 @@ static int print_data(struct libscols_table *tb,
if (!data)
data = "";
+ if (tb->format != SCOLS_FMT_HUMAN)
+ name = scols_cell_get_data(&cl->header);
+
is_last = is_last_column(cl);
+ if (is_last && scols_table_is_json(tb) &&
+ scols_table_is_tree(tb) && has_children(ln))
+ /* "children": [] is the real last value */
+ is_last = 0;
+
switch (tb->format) {
case SCOLS_FMT_RAW:
fputs_nonblank(data, tb->out);
@@ -463,37 +472,28 @@ static int print_data(struct libscols_table *tb,
return 0;
case SCOLS_FMT_EXPORT:
- fprintf(tb->out, "%s=", scols_cell_get_data(&cl->header));
+ fprintf(tb->out, "%s=", name);
fputs_quoted(data, tb->out);
if (!is_last)
fputs(colsep(tb), tb->out);
return 0;
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;
+ case SCOLS_JSON_STRING:
+ ul_jsonwrt_value_s(&tb->json, name, data, is_last);
+ break;
+ case SCOLS_JSON_NUMBER:
+ ul_jsonwrt_value_raw(&tb->json, name, data, is_last);
+ break;
+ case SCOLS_JSON_BOOLEAN:
+ ul_jsonwrt_value_boolean(&tb->json, name,
+ !*data ? 0 :
+ *data == '0' ? 0 :
+ *data == 'N' || *data == 'n' ? 0 : 1,
+ is_last);
+ break;
}
- if (!is_last)
- fputs(", ", tb->out);
return 0;
case SCOLS_FMT_HUMAN:
@@ -871,9 +871,17 @@ int __scols_print_range(struct libscols_table *tb,
int last = scols_iter_is_last(itr);
- fput_line_open(tb);
+ if (scols_table_is_json(tb))
+ ul_jsonwrt_object_open(&tb->json, NULL);
+
rc = print_line(tb, ln, buf);
- fput_line_close(tb, last, last);
+
+ if (scols_table_is_json(tb))
+ ul_jsonwrt_object_close(&tb->json, last);
+ else if (last == 0 && tb->no_linesep == 0) {
+ fputs(linesep(tb), tb->out);
+ tb->termlines_used++;
+ }
if (end && ln == end)
break;
@@ -905,16 +913,22 @@ static int print_tree_line(struct libscols_table *tb,
DBG(LINE, ul_debugobj(ln, " printing tree line"));
- fput_line_open(tb);
+ if (scols_table_is_json(tb))
+ ul_jsonwrt_object_open(&tb->json, NULL);
+
rc = print_line(tb, ln, buf);
if (rc)
return rc;
- if (has_children(ln))
- fput_children_open(tb);
-
- else {
- int last_in_tree = scols_walk_is_last(tb, ln);
+ if (has_children(ln)) {
+ if (scols_table_is_json(tb))
+ ul_jsonwrt_array_open(&tb->json, "children");
+ else {
+ /* between parent and child is separator */
+ fputs(linesep(tb), tb->out);
+ tb->termlines_used++;
+ }
+ } else {
int last;
/* terminate all open last children for JSON */
@@ -923,20 +937,20 @@ static int print_tree_line(struct libscols_table *tb,
last = (is_child(ln) && is_last_child(ln)) ||
(is_tree_root(ln) && is_last_tree_root(tb, ln));
- fput_line_close(tb, last, last_in_tree);
+ ul_jsonwrt_object_close(&tb->json, last);
if (last && is_child(ln))
- fput_children_close(tb);
-
- last_in_tree = 0;
+ ul_jsonwrt_array_close(&tb->json, last);
ln = ln->parent;
} while(ln && last);
- } else {
- /* standard output */
- last = (is_child(ln) && is_last_child(ln)) ||
- (is_group_child(ln) && is_last_group_child(ln));
+ } else if (tb->no_linesep == 0) {
+ int last_in_tree = scols_walk_is_last(tb, ln);
- fput_line_close(tb, last, last_in_tree);
+ if (last_in_tree == 0) {
+ /* standard output */
+ fputs(linesep(tb), tb->out);
+ tb->termlines_used++;
+ }
}
}
@@ -1029,8 +1043,8 @@ int __scols_initialize_printing(struct libscols_table *tb, struct libscols_buffe
extra_bufsz += tb->ncols; /* separator between columns */
break;
case SCOLS_FMT_JSON:
- if (tb->format == SCOLS_FMT_JSON)
- extra_bufsz += tb->nlines * 3; /* indentation */
+ ul_jsonwrt_init(&tb->json, tb->out, 0);
+ extra_bufsz += tb->nlines * 3; /* indentation */
/* fallthrough */
case SCOLS_FMT_EXPORT:
{
diff --git a/src/utils/libsmartcols/src/smartcolsP.h b/src/utils/libsmartcols/src/smartcolsP.h
index e36bb51..5e9b9ec 100644
--- a/src/utils/libsmartcols/src/smartcolsP.h
+++ b/src/utils/libsmartcols/src/smartcolsP.h
@@ -15,6 +15,7 @@
#include "list.h"
#include "strutils.h"
#include "color-names.h"
+#include "jsonwrt.h"
#include "debug.h"
#include "libsmartcols.h"
@@ -224,8 +225,8 @@ struct libscols_table {
struct libscols_symbols *symbols;
struct libscols_cell title; /* optional table title (for humans) */
- int indent; /* indentation counter */
- int indent_last_sep;/* last printed has been line separator */
+ struct ul_jsonwrt json; /* JSON formatting */
+
int format; /* SCOLS_FMT_* */
size_t termlines_used; /* printed line counter */
@@ -357,17 +358,6 @@ int __scols_print_range(struct libscols_table *tb,
struct libscols_iter *itr,
struct libscols_line *end);
-/*
- * fput.c
- */
-extern void fput_indent(struct libscols_table *tb);
-extern void fput_table_open(struct libscols_table *tb);
-extern void fput_table_close(struct libscols_table *tb);
-extern void fput_children_open(struct libscols_table *tb);
-extern void fput_children_close(struct libscols_table *tb);
-extern void fput_line_open(struct libscols_table *tb);
-extern void fput_line_close(struct libscols_table *tb, int last, int last_in_table);
-
static inline int is_tree_root(struct libscols_line *ln)
{
return ln && !ln->parent && !ln->parent_group;