summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:33:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:33:32 +0000
commit8bb05ac73a5b448b339ce0bc8d396c82c459b47f (patch)
tree1fdda006866bca20d41cb206767ea5241e36852f /lib
parentAdding debian version 2.39.3-11. (diff)
downloadutil-linux-8bb05ac73a5b448b339ce0bc8d396c82c459b47f.tar.xz
util-linux-8bb05ac73a5b448b339ce0bc8d396c82c459b47f.zip
Merging upstream version 2.40.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/buffer.c7
-rw-r--r--lib/c_strtod.c4
-rw-r--r--lib/caputils.c38
-rw-r--r--lib/colors.c10
-rw-r--r--lib/cpuset.c10
-rw-r--r--lib/env.c4
-rw-r--r--lib/exec_shell.c4
-rw-r--r--lib/fileeq.c2
-rw-r--r--lib/jsonwrt.c37
-rw-r--r--lib/loopdev.c165
-rw-r--r--lib/mbsalign.c21
-rw-r--r--lib/mbsedit.c13
-rw-r--r--lib/monotonic.c6
-rw-r--r--lib/pager.c38
-rw-r--r--lib/path.c53
-rw-r--r--lib/procfs.c5
-rw-r--r--lib/pty-session.c7
-rw-r--r--lib/sha1.c11
-rw-r--r--lib/sha256.c3
-rw-r--r--lib/shells.c103
-rw-r--r--lib/strutils.c60
-rw-r--r--lib/strv.c4
-rw-r--r--lib/terminal-colors.d.56
-rw-r--r--lib/timeutils.c255
-rw-r--r--lib/ttyutils.c78
25 files changed, 740 insertions, 204 deletions
diff --git a/lib/buffer.c b/lib/buffer.c
index fda2fc8..cc863fa 100644
--- a/lib/buffer.c
+++ b/lib/buffer.c
@@ -11,7 +11,7 @@
void ul_buffer_reset_data(struct ul_buffer *buf)
{
if (buf->begin)
- buf->begin[0] = '\0';
+ memset(buf->begin, 0, buf->sz);
buf->end = buf->begin;
if (buf->ptrs && buf->nptrs)
@@ -49,7 +49,7 @@ int ul_buffer_is_empty(struct ul_buffer *buf)
int ul_buffer_save_pointer(struct ul_buffer *buf, unsigned short ptr_idx)
{
if (ptr_idx >= buf->nptrs) {
- char **tmp = realloc(buf->ptrs, (ptr_idx + 1) * sizeof(char *));
+ char **tmp = reallocarray(buf->ptrs, ptr_idx + 1, sizeof(char *));
if (!tmp)
return -EINVAL;
@@ -134,12 +134,11 @@ int ul_buffer_append_data(struct ul_buffer *buf, const char *data, size_t sz)
if (!buf)
return -EINVAL;
- if (!data || !*data)
+ if (!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)
diff --git a/lib/c_strtod.c b/lib/c_strtod.c
index d25ee27..d7d8cbe 100644
--- a/lib/c_strtod.c
+++ b/lib/c_strtod.c
@@ -50,10 +50,10 @@ double c_strtod(char const *str, char **end)
return strtod_l(str, end, cl);
#elif defined(HAVE_USELOCALE)
/*
- * B) classic strtod(), but switch to "C" locale by uselocal()
+ * B) classic strtod(), but switch to "C" locale by uselocale()
*/
if (cl) {
- locale_t org_cl = uselocale(locale);
+ locale_t org_cl = uselocale(cl);
if (!org_cl)
return 0;
diff --git a/lib/caputils.c b/lib/caputils.c
index 987533a..23866c0 100644
--- a/lib/caputils.c
+++ b/lib/caputils.c
@@ -24,6 +24,7 @@
#include "caputils.h"
#include "pathnames.h"
#include "procfs.h"
+#include "nls.h"
static int test_cap(unsigned int cap)
{
@@ -87,6 +88,43 @@ int cap_last_cap(void)
return cap;
}
+void cap_permitted_to_ambient(void)
+{
+ /* We use capabilities system calls to propagate the permitted
+ * capabilities into the ambient set because we may have
+ * already forked so be in async-signal-safe context. */
+ struct __user_cap_header_struct header = {
+ .version = _LINUX_CAPABILITY_VERSION_3,
+ .pid = 0,
+ };
+ struct __user_cap_data_struct payload[_LINUX_CAPABILITY_U32S_3] = {{ 0 }};
+ uint64_t effective, cap;
+
+ if (capget(&header, payload) < 0)
+ err(EXIT_FAILURE, _("capget failed"));
+
+ /* In order the make capabilities ambient, we first need to ensure
+ * that they are all inheritable. */
+ payload[0].inheritable = payload[0].permitted;
+ payload[1].inheritable = payload[1].permitted;
+
+ if (capset(&header, payload) < 0)
+ err(EXIT_FAILURE, _("capset failed"));
+
+ effective = ((uint64_t)payload[1].effective << 32) | (uint64_t)payload[0].effective;
+
+ for (cap = 0; cap < (sizeof(effective) * 8); cap++) {
+ /* This is the same check as cap_valid(), but using
+ * the runtime value for the last valid cap. */
+ if (cap > (uint64_t) cap_last_cap())
+ continue;
+
+ if ((effective & (1ULL << cap))
+ && prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) < 0)
+ err(EXIT_FAILURE, _("prctl(PR_CAP_AMBIENT) failed"));
+ }
+}
+
#ifdef TEST_PROGRAM_CAPUTILS
int main(int argc, char *argv[])
{
diff --git a/lib/colors.c b/lib/colors.c
index 532e339..0928770 100644
--- a/lib/colors.c
+++ b/lib/colors.c
@@ -357,12 +357,10 @@ static char *colors_get_homedir(char *buf, size_t bufsz)
/*
* Adds one color sequence to array with color scheme.
- * When returning success (0) this function takes ownership of
- * @seq and @name, which have to be allocated strings.
*/
static int colors_add_scheme(struct ul_color_ctl *cc,
- char *name,
- char *seq0)
+ const char *name,
+ const char *seq0)
{
struct ul_color_scheme *cs = NULL;
char *seq = NULL;
@@ -380,8 +378,8 @@ static int colors_add_scheme(struct ul_color_ctl *cc,
/* enlarge the array */
if (cc->nschemes == cc->schemes_sz) {
- void *tmp = realloc(cc->schemes, (cc->nschemes + 10)
- * sizeof(struct ul_color_scheme));
+ void *tmp = reallocarray(cc->schemes, cc->nschemes + 10,
+ sizeof(struct ul_color_scheme));
if (!tmp)
goto err;
cc->schemes = tmp;
diff --git a/lib/cpuset.c b/lib/cpuset.c
index 098b8e5..533b8ab 100644
--- a/lib/cpuset.c
+++ b/lib/cpuset.c
@@ -287,7 +287,7 @@ static int nextnumber(const char *str, char **end, unsigned int *result)
*/
int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail)
{
- size_t max = cpuset_nbits(setsize);
+ const size_t max = cpuset_nbits(setsize);
const char *p, *q;
char *end = NULL;
@@ -326,8 +326,12 @@ int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail)
if (!(a <= b))
return 1;
while (a <= b) {
- if (fail && (a >= max))
- return 2;
+ if (a >= max) {
+ if (fail)
+ return 2;
+ else
+ break;
+ }
CPU_SET_S(a, setsize, set);
a += s;
}
diff --git a/lib/env.c b/lib/env.c
index 2b3395c..2bdfe56 100644
--- a/lib/env.c
+++ b/lib/env.c
@@ -159,7 +159,7 @@ void __sanitize_env(struct ul_env_list **org)
if (strncmp(*cur, *bad, strlen(*bad)) == 0) {
if (org)
*org = env_list_add(*org, *cur);
- last = remote_entry(envp, cur - envp, last);
+ last = remove_entry(envp, cur - envp, last);
cur--;
break;
}
@@ -174,7 +174,7 @@ void __sanitize_env(struct ul_env_list **org)
continue; /* OK */
if (org)
*org = env_list_add(*org, *cur);
- last = remote_entry(envp, cur - envp, last);
+ last = remove_entry(envp, cur - envp, last);
cur--;
break;
}
diff --git a/lib/exec_shell.c b/lib/exec_shell.c
index 6fef6c7..96d3e95 100644
--- a/lib/exec_shell.c
+++ b/lib/exec_shell.c
@@ -42,9 +42,7 @@ void __attribute__((__noreturn__)) exec_shell(void)
shellc = xstrdup(shell);
shell_basename = basename(shellc);
- arg0 = xmalloc(strlen(shell_basename) + 2);
- arg0[0] = '-';
- strcpy(arg0 + 1, shell_basename);
+ xasprintf(&arg0, "-%s", shell_basename);
execl(shell, arg0, (char *)NULL);
errexec(shell);
diff --git a/lib/fileeq.c b/lib/fileeq.c
index af5b4e3..2a74af8 100644
--- a/lib/fileeq.c
+++ b/lib/fileeq.c
@@ -79,9 +79,9 @@ enum {
};
struct ul_fileeq_method {
- int id;
const char *name; /* name used by applications */
const char *kname; /* name used by kernel crypto */
+ int id;
short digsiz;
};
diff --git a/lib/jsonwrt.c b/lib/jsonwrt.c
index 8ca1d4d..243ed82 100644
--- a/lib/jsonwrt.c
+++ b/lib/jsonwrt.c
@@ -26,12 +26,12 @@
* }
* }
*/
-static void fputs_quoted_case_json(const char *data, FILE *out, int dir)
+static void fputs_quoted_case_json(const char *data, FILE *out, int dir, size_t size)
{
const char *p;
fputc('"', out);
- for (p = data; p && *p; p++) {
+ for (p = data; p && *p && (!size || p < data + size); p++) {
const unsigned int c = (unsigned int) *p;
@@ -98,9 +98,9 @@ static void fputs_quoted_case_json(const char *data, FILE *out, int dir)
fputc('"', out);
}
-#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)
+#define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0, 0)
+#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1, 0)
+#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1, 0)
void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent)
{
@@ -154,12 +154,6 @@ void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type)
void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type)
{
- if (fmt->indent == 1) {
- fputs("\n}\n", fmt->out);
- fmt->indent--;
- fmt->after_close = 1;
- return;
- }
assert(fmt->indent > 0);
switch (type) {
@@ -168,6 +162,8 @@ void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type)
fputc('\n', fmt->out);
ul_jsonwrt_indent(fmt);
fputs("}", fmt->out);
+ if (fmt->indent == 0)
+ fputs("\n", fmt->out);
break;
case UL_JSON_ARRAY:
fmt->indent--;
@@ -204,6 +200,17 @@ void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt,
ul_jsonwrt_value_close(fmt);
}
+void ul_jsonwrt_value_s_sized(struct ul_jsonwrt *fmt,
+ const char *name, const char *data, size_t size)
+{
+ ul_jsonwrt_value_open(fmt, name);
+ if (data && *data)
+ fputs_quoted_case_json(data, fmt->out, 0, size);
+ else
+ fputs("null", fmt->out);
+ ul_jsonwrt_value_close(fmt);
+}
+
void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt,
const char *name, uint64_t data)
{
@@ -212,6 +219,14 @@ void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt,
ul_jsonwrt_value_close(fmt);
}
+void ul_jsonwrt_value_double(struct ul_jsonwrt *fmt,
+ const char *name, long double data)
+{
+ ul_jsonwrt_value_open(fmt, name);
+ fprintf(fmt->out, "%Lg", data);
+ ul_jsonwrt_value_close(fmt);
+}
+
void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt,
const char *name, int data)
{
diff --git a/lib/loopdev.c b/lib/loopdev.c
index dd9ead3..c72fb2c 100644
--- a/lib/loopdev.c
+++ b/lib/loopdev.c
@@ -116,7 +116,9 @@ int loopcxt_set_device(struct loopdev_cxt *lc, const char *device)
DBG(CXT, ul_debugobj(lc, "closing old open fd"));
}
lc->fd = -1;
- lc->mode = 0;
+ lc->is_lost = 0;
+ lc->devno = 0;
+ lc->mode = O_RDONLY;
lc->blocksize = 0;
lc->has_info = 0;
lc->info_failed = 0;
@@ -153,6 +155,28 @@ int loopcxt_has_device(struct loopdev_cxt *lc)
return lc && *lc->device;
}
+dev_t loopcxt_get_devno(struct loopdev_cxt *lc)
+{
+ if (!lc || !loopcxt_has_device(lc))
+ return 0;
+ if (!lc->devno)
+ lc->devno = sysfs_devname_to_devno(lc->device);
+ return lc->devno;
+}
+
+int loopcxt_is_lost(struct loopdev_cxt *lc)
+{
+ if (!lc || !loopcxt_has_device(lc))
+ return 0;
+ if (lc->is_lost)
+ return 1;
+
+ lc->is_lost = access(lc->device, F_OK) != 0
+ && loopcxt_get_devno(lc) != 0;
+
+ return lc->is_lost;
+}
+
/*
* @lc: context
* @flags: LOOPDEV_FL_* flags
@@ -165,12 +189,6 @@ int loopcxt_has_device(struct loopdev_cxt *lc)
*
* * LO_FLAGS_* are kernel flags used for LOOP_{SET,GET}_STAT64 ioctls
*
- * Note about LOOPDEV_FL_{RDONLY,RDWR} flags. These flags are used for open(2)
- * syscall to open loop device. By default is the device open read-only.
- *
- * The exception is loopcxt_setup_device(), where the device is open read-write
- * if LO_FLAGS_READ_ONLY flags is not set (see loopcxt_set_flags()).
- *
* Returns: <0 on error, 0 on success.
*/
int loopcxt_init(struct loopdev_cxt *lc, int flags)
@@ -271,7 +289,7 @@ static struct path_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc)
return NULL;
if (!lc->sysfs) {
- dev_t devno = sysfs_devname_to_devno(lc->device);
+ dev_t devno = loopcxt_get_devno(lc);
if (!devno) {
DBG(CXT, ul_debugobj(lc, "sysfs: failed devname to devno"));
return NULL;
@@ -285,28 +303,50 @@ static struct path_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc)
return lc->sysfs;
}
-/*
- * @lc: context
- *
- * Returns: file descriptor to the open loop device or <0 on error. The mode
- * depends on LOOPDEV_FL_{RDWR,RDONLY} context flags. Default is
- * read-only.
- */
-int loopcxt_get_fd(struct loopdev_cxt *lc)
+static int __loopcxt_get_fd(struct loopdev_cxt *lc, mode_t mode)
{
+ int old = -1;
+
if (!lc || !*lc->device)
return -EINVAL;
+ /* It's okay to return a FD with read-write permissions if someone
+ * asked for read-only, but you shouldn't do the opposite.
+ *
+ * (O_RDONLY is a widely usable default.)
+ */
+ if (lc->fd >= 0 && mode == O_RDWR && lc->mode == O_RDONLY) {
+ DBG(CXT, ul_debugobj(lc, "closing already open device (mode mismatch)"));
+ old = lc->fd;
+ lc->fd = -1;
+ }
+
if (lc->fd < 0) {
- lc->mode = lc->flags & LOOPDEV_FL_RDWR ? O_RDWR : O_RDONLY;
+ lc->mode = mode;
lc->fd = open(lc->device, lc->mode | O_CLOEXEC);
DBG(CXT, ul_debugobj(lc, "open %s [%s]: %m", lc->device,
- lc->flags & LOOPDEV_FL_RDWR ? "rw" : "ro"));
+ mode == O_RDONLY ? "ro" :
+ mode == O_RDWR ? "rw" : "??"));
+
+ if (lc->fd < 0 && old >= 0) {
+ /* restore original on error */
+ lc->fd = old;
+ old = -1;
+ }
}
+
+ if (old >= 0)
+ close(old);
return lc->fd;
}
-int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode)
+/* default is read-only file descriptor, it's enough for all ioctls */
+int loopcxt_get_fd(struct loopdev_cxt *lc)
+{
+ return __loopcxt_get_fd(lc, O_RDONLY);
+}
+
+int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, mode_t mode)
{
if (!lc)
return -EINVAL;
@@ -400,13 +440,6 @@ static int loopiter_set_device(struct loopdev_cxt *lc, const char *device)
!(lc->iter.flags & LOOPITER_FL_FREE))
return 0; /* caller does not care about device status */
- if (!is_loopdev(lc->device)) {
- DBG(ITER, ul_debugobj(&lc->iter, "%s does not exist", lc->device));
- return -errno;
- }
-
- DBG(ITER, ul_debugobj(&lc->iter, "%s exist", lc->device));
-
used = loopcxt_get_offset(lc, NULL) == 0;
if ((lc->iter.flags & LOOPITER_FL_USED) && used)
@@ -479,7 +512,7 @@ static int loop_scandir(const char *dirname, int **ary, int hasprefix)
arylen += 1;
- tmp = realloc(*ary, arylen * sizeof(int));
+ tmp = reallocarray(*ary, arylen, sizeof(int));
if (!tmp) {
free(*ary);
*ary = NULL;
@@ -749,6 +782,26 @@ char *loopcxt_get_backing_file(struct loopdev_cxt *lc)
/*
* @lc: context
+ *
+ * Returns (allocated) string with loop reference. The same as backing file by
+ * default.
+ */
+char *loopcxt_get_refname(struct loopdev_cxt *lc)
+{
+ char *res = NULL;
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+
+ if (lo) {
+ lo->lo_file_name[LO_NAME_SIZE - 1] = '\0';
+ res = strdup((char *) lo->lo_file_name);
+ }
+
+ DBG(CXT, ul_debugobj(lc, "get_refname [%s]", res));
+ return res;
+}
+
+/*
+ * @lc: context
* @offset: returns offset number for the given device
*
* Returns: <0 on error, 0 on success
@@ -840,7 +893,7 @@ int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size)
/*
* @lc: context
- * @devno: returns encryption type
+ * @type: returns encryption type
*
* Cryptoloop is DEPRECATED!
*
@@ -865,7 +918,6 @@ int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type)
/*
* @lc: context
- * @devno: returns crypt name
*
* Cryptoloop is DEPRECATED!
*
@@ -1182,6 +1234,28 @@ int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags)
/*
* @lc: context
+ * @refname: reference name (used to overwrite lo_file_name where is backing
+ * file by default)
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_refname(struct loopdev_cxt *lc, const char *refname)
+{
+ if (!lc)
+ return -EINVAL;
+
+ memset(lc->config.info.lo_file_name, 0, sizeof(lc->config.info.lo_file_name));
+ if (refname)
+ xstrncpy((char *)lc->config.info.lo_file_name, refname, LO_NAME_SIZE);
+
+ DBG(CXT, ul_debugobj(lc, "set refname=%s", (char *)lc->config.info.lo_file_name));
+ return 0;
+}
+
+/*
+ * @lc: context
* @filename: backing file path (the path will be canonicalized)
*
* The setting is removed by loopcxt_set_device() loopcxt_next()!
@@ -1197,9 +1271,10 @@ int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename)
if (!lc->filename)
return -errno;
- xstrncpy((char *)lc->config.info.lo_file_name, lc->filename, LO_NAME_SIZE);
+ if (!lc->config.info.lo_file_name[0])
+ loopcxt_set_refname(lc, lc->filename);
- DBG(CXT, ul_debugobj(lc, "set backing file=%s", lc->config.info.lo_file_name));
+ DBG(CXT, ul_debugobj(lc, "set backing file=%s", lc->filename));
return 0;
}
@@ -1314,7 +1389,8 @@ static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd)
*/
int loopcxt_setup_device(struct loopdev_cxt *lc)
{
- int file_fd, dev_fd, mode = O_RDWR, flags = O_CLOEXEC;
+ int file_fd, dev_fd;
+ mode_t flags = O_CLOEXEC, mode = O_RDWR;
int rc = -1, cnt = 0;
int errsv = 0;
int fallback = 0;
@@ -1344,25 +1420,22 @@ int loopcxt_setup_device(struct loopdev_cxt *lc)
}
DBG(SETUP, ul_debugobj(lc, "backing file open: OK"));
- if (lc->fd != -1 && lc->mode != mode) {
- DBG(SETUP, ul_debugobj(lc, "closing already open device (mode mismatch)"));
- close(lc->fd);
- lc->fd = -1;
- lc->mode = 0;
- }
-
- if (mode == O_RDONLY) {
- lc->flags |= LOOPDEV_FL_RDONLY; /* open() mode */
+ if (mode == O_RDONLY)
lc->config.info.lo_flags |= LO_FLAGS_READ_ONLY; /* kernel loopdev mode */
- } else {
- lc->flags |= LOOPDEV_FL_RDWR; /* open() mode */
+ else
lc->config.info.lo_flags &= ~LO_FLAGS_READ_ONLY;
- lc->flags &= ~LOOPDEV_FL_RDONLY;
- }
do {
errno = 0;
- dev_fd = loopcxt_get_fd(lc);
+
+ /* For the ioctls, it's enough to use O_RDONLY, but udevd
+ * monitor devices by inotify, and udevd needs IN_CLOSE_WRITE
+ * event to trigger probing of the new device.
+ *
+ * The mode used for the device does not have to match the mode
+ * used for the backing file.
+ */
+ dev_fd = __loopcxt_get_fd(lc, O_RDWR);
if (dev_fd >= 0 || lc->control_ok == 0)
break;
if (errno != EACCES && errno != ENOENT)
diff --git a/lib/mbsalign.c b/lib/mbsalign.c
index 7b8f5a6..b4ab7be 100644
--- a/lib/mbsalign.c
+++ b/lib/mbsalign.c
@@ -310,12 +310,33 @@ char *mbs_invalid_encode_to_buffer(const char *s, size_t *width, char *buf)
return buf;
}
+/*
+ * Guess size
+ */
size_t mbs_safe_encode_size(size_t bytes)
{
return (bytes * 4) + 1;
}
/*
+ * Count size of the original string in bytes (count \x?? as one byte)
+ */
+size_t mbs_safe_decode_size(const char *p)
+{
+ size_t bytes = 0;
+
+ while (p && *p) {
+ if (*p == '\\' && *(p + 1) == 'x' &&
+ isxdigit(*(p + 2)) && isxdigit(*(p + 3)))
+ p += 4;
+ else
+ p++;
+ bytes++;
+ }
+ return bytes;
+}
+
+/*
* Returns allocated string where all control and non-printable chars are
* replaced with \x?? hex sequence.
*/
diff --git a/lib/mbsedit.c b/lib/mbsedit.c
index ecfa9f4..9cf4f0f 100644
--- a/lib/mbsedit.c
+++ b/lib/mbsedit.c
@@ -157,13 +157,14 @@ static size_t mbs_insert(char *str, wint_t c, size_t *ncells)
#ifdef HAVE_WIDECHAR
wchar_t wc = (wchar_t) c;
- char in_buf[MB_CUR_MAX];
+ in = malloc(MB_CUR_MAX);
+ if (!in)
+ return -1;
- n = wctomb(in_buf, wc);
+ n = wctomb(in, wc);
if (n == (size_t) -1)
- return n;
+ goto out;
*ncells = wcwidth(wc);
- in = in_buf;
#else
*ncells = 1;
in = (char *) &c;
@@ -173,6 +174,10 @@ static size_t mbs_insert(char *str, wint_t c, size_t *ncells)
memmove(str + n, str, bytes);
memcpy(str, in, n);
str[bytes + n] = '\0';
+out:
+#ifdef HAVE_WIDECHAR
+ free(in);
+#endif
return n;
}
diff --git a/lib/monotonic.c b/lib/monotonic.c
index cd554dd..63feec0 100644
--- a/lib/monotonic.c
+++ b/lib/monotonic.c
@@ -18,10 +18,10 @@
int get_boot_time(struct timeval *boot_time)
{
#ifdef CLOCK_BOOTTIME
- struct timespec hires_uptime;
- struct timeval lores_uptime;
+ struct timespec hires_uptime = { 0 };
+ struct timeval lores_uptime = { 0 };
#endif
- struct timeval now;
+ struct timeval now = { 0 };
#ifdef HAVE_SYSINFO
struct sysinfo info;
#endif
diff --git a/lib/pager.c b/lib/pager.c
index 9429032..98814b5 100644
--- a/lib/pager.c
+++ b/lib/pager.c
@@ -85,7 +85,9 @@ static int start_command(struct child_process *cmd)
close(cmd->in);
}
- cmd->preexec_cb();
+ if (cmd->preexec_cb)
+ cmd->preexec_cb();
+
execvp(cmd->argv[0], (char *const*) cmd->argv);
errexec(cmd->argv[0]);
}
@@ -140,7 +142,7 @@ static int finish_command(struct child_process *cmd)
return wait_or_whine(cmd->pid);
}
-static void pager_preexec(void)
+static void pager_preexec_less(void)
{
/*
* Work around bug in "less" by not starting it until we
@@ -178,35 +180,45 @@ static void wait_for_pager_signal(int signo)
static int has_command(const char *cmd)
{
const char *path;
- char *p, *s;
+ char *b, *c, *p, *s;
int rc = 0;
if (!cmd)
goto done;
- if (*cmd == '/') {
- rc = access(cmd, X_OK) == 0;
+
+ c = xstrdup(cmd);
+ if (!c)
goto done;
+ b = strtok(c, " "); /* cmd may contain options */
+ if (!b)
+ goto cleanup;
+
+ if (*b == '/') {
+ rc = access(b, X_OK) == 0;
+ goto cleanup;
}
path = getenv("PATH");
if (!path)
- goto done;
+ goto cleanup;
p = xstrdup(path);
if (!p)
- goto done;
+ goto cleanup;
- for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
+ for (s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
int fd = open(s, O_RDONLY|O_CLOEXEC);
if (fd < 0)
continue;
- rc = faccessat(fd, cmd, X_OK, 0) == 0;
+ rc = faccessat(fd, b, X_OK, 0) == 0;
close(fd);
if (rc)
break;
}
free(p);
+cleanup:
+ free(c);
done:
- /*fprintf(stderr, "has PAGER %s rc=%d\n", cmd, rc);*/
+ /*fprintf(stderr, "has PAGER '%s': rc=%d\n", cmd, rc);*/
return rc;
}
@@ -230,7 +242,11 @@ static void __setup_pager(void)
pager_argv[2] = pager;
pager_process.argv = pager_argv;
pager_process.in = -1;
- pager_process.preexec_cb = pager_preexec;
+
+ if (!strncmp(pager, "less", 4))
+ pager_process.preexec_cb = pager_preexec_less;
+ else
+ pager_process.preexec_cb = NULL;
if (start_command(&pager_process))
return;
diff --git a/lib/path.c b/lib/path.c
index 4aceda1..202f19a 100644
--- a/lib/path.c
+++ b/lib/path.c
@@ -996,38 +996,25 @@ int ul_path_next_dirent(struct path_cxt *pc, DIR **sub, const char *dirname, str
return 1;
}
-/*
- * Like fopen() but, @path is always prefixed by @prefix. This function is
- * useful in case when ul_path_* API is overkill.
- */
-FILE *ul_prefix_fopen(const char *prefix, const char *path, const char *mode)
-{
- char buf[PATH_MAX];
-
- if (!path)
- return NULL;
- if (!prefix)
- return fopen(path, mode);
- if (*path == '/')
- path++;
-
- snprintf(buf, sizeof(buf), "%s/%s", prefix, path);
- return fopen(buf, mode);
-}
-
#ifdef HAVE_CPU_SET_T
static int ul_path_cpuparse(struct path_cxt *pc, cpu_set_t **set, int maxcpus, int islist, const char *path, va_list ap)
{
FILE *f;
size_t setsize, len = maxcpus * 7;
- char buf[len];
+ char *buf;
int rc;
*set = NULL;
+ buf = malloc(len);
+ if (!buf)
+ return -ENOMEM;
+
f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
- if (!f)
- return -errno;
+ if (!f) {
+ rc = -errno;
+ goto out;
+ }
if (fgets(buf, len, f) == NULL) {
errno = EIO;
@@ -1038,32 +1025,38 @@ static int ul_path_cpuparse(struct path_cxt *pc, cpu_set_t **set, int maxcpus, i
fclose(f);
if (rc)
- return rc;
+ goto out;
len = strlen(buf);
if (len > 0 && buf[len - 1] == '\n')
buf[len - 1] = '\0';
*set = cpuset_alloc(maxcpus, &setsize, NULL);
- if (!*set)
- return -ENOMEM;
+ if (!*set) {
+ rc = -EINVAL;
+ goto out;
+ }
if (islist) {
if (cpulist_parse(buf, *set, setsize, 0)) {
- cpuset_free(*set);
errno = EINVAL;
rc = -errno;
- return rc;
+ goto out;
}
} else {
if (cpumask_parse(buf, *set, setsize)) {
- cpuset_free(*set);
errno = EINVAL;
rc = -errno;
- return rc;
+ goto out;
}
}
- return 0;
+ rc = 0;
+
+out:
+ if (rc)
+ cpuset_free(*set);
+ free(buf);
+ return rc;
}
int ul_path_readf_cpuset(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
diff --git a/lib/procfs.c b/lib/procfs.c
index aff20fb..59f40ff 100644
--- a/lib/procfs.c
+++ b/lib/procfs.c
@@ -169,6 +169,11 @@ ssize_t procfs_process_get_stat(struct path_cxt *pc, char *buf, size_t bufsz)
return procfs_process_get_data_for(pc, buf, bufsz, "stat");
}
+ssize_t procfs_process_get_syscall(struct path_cxt *pc, char *buf, size_t bufsz)
+{
+ return procfs_process_get_data_for(pc, buf, bufsz, "syscall");
+}
+
int procfs_process_get_stat_nth(struct path_cxt *pc, int n, uintmax_t *re)
{
ssize_t rc;
diff --git a/lib/pty-session.c b/lib/pty-session.c
index 3849065..815264d 100644
--- a/lib/pty-session.c
+++ b/lib/pty-session.c
@@ -241,6 +241,7 @@ int ul_pty_signals_setup(struct ul_pty *pty)
sigaddset(&ourset, SIGCHLD);
sigaddset(&ourset, SIGWINCH);
sigaddset(&ourset, SIGALRM);
+ sigaddset(&ourset, SIGHUP);
sigaddset(&ourset, SIGTERM);
sigaddset(&ourset, SIGINT);
sigaddset(&ourset, SIGQUIT);
@@ -584,6 +585,8 @@ static int handle_signal(struct ul_pty *pty, int fd)
&info, (void *) &pty->win);
}
break;
+ case SIGHUP:
+ /* fallthrough */
case SIGTERM:
/* fallthrough */
case SIGINT:
@@ -641,7 +644,7 @@ int ul_pty_proxy_master(struct ul_pty *pty)
/* note, callback usually updates @next_callback_time */
if (timerisset(&pty->next_callback_time)) {
- struct timeval now;
+ struct timeval now = { 0 };;
DBG(IO, ul_debugobj(pty, " callback requested"));
gettime_monotonic(&now);
@@ -654,7 +657,7 @@ int ul_pty_proxy_master(struct ul_pty *pty)
/* set timeout */
if (timerisset(&pty->next_callback_time)) {
- struct timeval now, rest;
+ struct timeval now = { 0 }, rest = { 0 };
gettime_monotonic(&now);
timersub(&pty->next_callback_time, &now, &rest);
diff --git a/lib/sha1.c b/lib/sha1.c
index eedeaa8..3edff12 100644
--- a/lib/sha1.c
+++ b/lib/sha1.c
@@ -1,4 +1,7 @@
/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
* SHA-1 in C by Steve Reid <steve@edmweb.com>
* 100% Public Domain
*
@@ -153,7 +156,15 @@ void ul_SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
state[3] += d;
state[4] += e;
/* Wipe variables */
+#ifdef HAVE_EXPLICIT_BZERO
+ explicit_bzero(&a, sizeof(a));
+ explicit_bzero(&b, sizeof(b));
+ explicit_bzero(&c, sizeof(c));
+ explicit_bzero(&d, sizeof(d));
+ explicit_bzero(&e, sizeof(e));
+#else
a = b = c = d = e = 0;
+#endif
#ifdef UL_SHA1HANDSOFF
memset(block, '\0', sizeof(block));
#endif
diff --git a/lib/sha256.c b/lib/sha256.c
index 934303c..10877d2 100644
--- a/lib/sha256.c
+++ b/lib/sha256.c
@@ -1,4 +1,7 @@
/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
* public domain sha256 crypt implementation
*
* original sha crypt design: http://people.redhat.com/drepper/SHA-crypt.txt
diff --git a/lib/shells.c b/lib/shells.c
index 6693ab0..18e4cca 100644
--- a/lib/shells.c
+++ b/lib/shells.c
@@ -9,20 +9,11 @@
#include "closestream.h"
#include "shells.h"
-/*
- * is_known_shell() -- if the given shell appears in /etc/shells
- * or vendor defined files.
- * Return 1 if found and return 0 if not found.
- */
-extern int is_known_shell(const char *shell_name)
-{
- int ret = 0;
-
#if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR)
- size_t size = 0;
+static econf_file *open_etc_shells(void)
+{
econf_err error;
- char **keys;
- econf_file *key_file;
+ econf_file *key_file = NULL;
error = econf_readDirs(&key_file,
_PATH_VENDORDIR,
@@ -35,31 +26,84 @@ extern int is_known_shell(const char *shell_name)
syslog(LOG_ALERT,
_("Cannot parse shells files: %s"),
econf_errString(error));
- exit(EXIT_FAILURE);
+ return NULL;
}
- error = econf_getKeys(key_file, NULL, &size, &keys);
- if (error) {
- syslog(LOG_ALERT,
- _("Cannot evaluate entries in shells files: %s"),
- econf_errString(error));
- econf_free (key_file);
- exit(EXIT_FAILURE);
- }
+ return key_file;
+}
+#endif
- for (size_t i = 0; i < size; i++) {
- if (strcmp (keys[i], shell_name) == 0) {
- ret = 1;
- break;
- }
- }
- econf_free (key_file);
+/*
+ * print_shells () -- /etc/shells is outputted to stdout.
+ */
+extern void print_shells(FILE *out, const char *format)
+{
+#if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR)
+ size_t size = 0;
+ econf_err error;
+ char **keys = NULL;
+ econf_file *key_file = open_etc_shells();
+
+ if (!key_file)
+ return;
+
+ error = econf_getKeys(key_file, NULL, &size, &keys);
+ if (error) {
+ econf_free(key_file);
+ errx(EXIT_FAILURE,
+ _("Cannot evaluate entries in shells files: %s"),
+ econf_errString(error));
+ }
+
+ for (size_t i = 0; i < size; i++) {
+ fprintf(out, format, keys[i]);
+ }
+ econf_free(keys);
+ econf_free(key_file);
#else
- char *s;
+ char *s;
+
+ while ((s = getusershell()))
+ fprintf(out, format, s);
+ endusershell();
+#endif
+}
+
+
+/*
+ * is_known_shell() -- if the given shell appears in /etc/shells
+ * or vendor defined files.
+ * Return 1 if found and return 0 if not found.
+ */
+extern int is_known_shell(const char *shell_name)
+{
+ int ret = 0;
if (!shell_name)
return 0;
+#if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR)
+ char *val = NULL;
+ econf_err error;
+ econf_file *key_file = open_etc_shells();
+
+ if (!key_file)
+ return 0;
+
+ error = econf_getStringValue (key_file, NULL, shell_name, &val);
+ if (error) {
+ if (error != ECONF_NOKEY)
+ syslog(LOG_ALERT,
+ _("Cannot evaluate entries in shells files: %s"),
+ econf_errString(error));
+ } else
+ ret = 1;
+
+ free(val);
+ econf_free(key_file);
+#else
+ char *s;
+
setusershell();
while ((s = getusershell())) {
if (*s != '#' && strcmp(shell_name, s) == 0) {
@@ -71,4 +115,3 @@ extern int is_known_shell(const char *shell_name)
#endif
return ret;
}
-
diff --git a/lib/strutils.c b/lib/strutils.c
index 6229a6e..9ea5da7 100644
--- a/lib/strutils.c
+++ b/lib/strutils.c
@@ -456,21 +456,28 @@ err:
errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
}
-long double strtold_or_err(const char *str, const char *errmesg)
+int ul_strtold(const char *str, long double *num)
{
- double num;
char *end = NULL;
errno = 0;
if (str == NULL || *str == '\0')
- goto err;
- num = strtold(str, &end);
+ return -(errno = EINVAL);
+ *num = strtold(str, &end);
- if (errno || str == end || (end && *end))
- goto err;
+ if (errno != 0)
+ return -errno;
+ if (str == end || (end && *end))
+ return -(errno = EINVAL);
+ return 0;
+}
- return num;
-err:
+long double strtold_or_err(const char *str, const char *errmesg)
+{
+ long double num = 0;
+
+ if (ul_strtold(str, &num) == 0)
+ return num;
if (errno == ERANGE)
err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
@@ -1010,6 +1017,34 @@ int strappend(char **a, const char *b)
return 0;
}
+/* the hybrid version of strfconcat and strappend. */
+int strfappend(char **a, const char *format, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, format);
+ res = strvfappend(a, format, ap);
+ va_end(ap);
+
+ return res;
+}
+
+extern int strvfappend(char **a, const char *format, va_list ap)
+{
+ char *val;
+ int sz;
+ int res;
+
+ sz = vasprintf(&val, format, ap);
+ if (sz < 0)
+ return -errno;
+
+ res = strappend(a, val);
+ free(val);
+ return res;
+}
+
static size_t strcspn_escaped(const char *s, const char *reject)
{
int escaped = 0;
@@ -1386,6 +1421,15 @@ int main(int argc, char *argv[])
printf("\"%s\" --> \"%s\"\n", argv[2], ul_strchr_escaped(argv[2], *argv[3]));
return EXIT_SUCCESS;
+ } else if (argc == 2 && strcmp(argv[1], "--next-string") == 0) {
+ char *buf = "abc\0Y\0\0xyz\0X";
+ char *end = buf + 12;
+ char *p = buf;
+
+ do {
+ printf("str: '%s'\n", p);
+ } while ((p = ul_next_string(p, end)));
+
} else {
fprintf(stderr, "usage: %1$s --size <number>[suffix]\n"
" %1$s --cmp-paths <path> <path>\n"
diff --git a/lib/strv.c b/lib/strv.c
index c306e38..fd84fe3 100644
--- a/lib/strv.c
+++ b/lib/strv.c
@@ -74,7 +74,7 @@ unsigned strv_length(char * const *l) {
return n;
}
-char **strv_new_ap(const char *x, va_list ap) {
+static char **strv_new_ap(const char *x, va_list ap) {
const char *s;
char **a;
unsigned n = 0, i = 0;
@@ -265,7 +265,7 @@ int strv_push(char ***l, char *value) {
if (m < n)
return -ENOMEM;
- c = realloc(*l, sizeof(char *) * m);
+ c = reallocarray(*l, m, sizeof(char *));
if (!c)
return -ENOMEM;
diff --git a/lib/terminal-colors.d.5 b/lib/terminal-colors.d.5
index 4b98389..1de42f4 100644
--- a/lib/terminal-colors.d.5
+++ b/lib/terminal-colors.d.5
@@ -2,12 +2,12 @@
.\" Title: terminal-colors.d
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-10-23
+.\" Date: 2024-01-31
.\" Manual: File formats
-.\" Source: util-linux 2.39.3
+.\" Source: util-linux 2.40
.\" Language: English
.\"
-.TH "TERMINAL\-COLORS.D" "5" "2023-10-23" "util\-linux 2.39.3" "File formats"
+.TH "TERMINAL\-COLORS.D" "5" "2024-01-31" "util\-linux 2.40" "File formats"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/lib/timeutils.c b/lib/timeutils.c
index 34c7c8d..f53ec8f 100644
--- a/lib/timeutils.c
+++ b/lib/timeutils.c
@@ -161,6 +161,23 @@ static int parse_subseconds(const char *t, usec_t *usec)
return 0;
}
+static const char *parse_epoch_seconds(const char *t, struct tm *tm)
+{
+ int64_t s;
+ time_t st;
+ int f, c;
+
+ f = sscanf(t, "%"SCNd64"%n", &s, &c);
+ if (f < 1)
+ return NULL;
+ st = s;
+ if ((int64_t) st < s)
+ return NULL;
+ if (!localtime_r(&st, tm))
+ return NULL;
+ return t + c;
+}
+
static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec)
{
static const struct {
@@ -254,7 +271,7 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec)
goto finish;
} else if (t[0] == '@') {
- k = strptime(t + 1, "%s", &tm);
+ k = parse_epoch_seconds(t + 1, &tm);
if (k && *k == 0)
goto finish;
else if (k && parse_subseconds(k, &ret) == 0)
@@ -373,11 +390,13 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec)
ret += (usec_t) x * USEC_PER_SEC;
+ if (minus > ret)
+ return -ERANGE;
+ if ((ret + plus) < ret)
+ return -ERANGE;
+
ret += plus;
- if (ret > minus)
- ret -= minus;
- else
- ret = 0;
+ ret -= minus;
*usec = ret;
@@ -448,8 +467,9 @@ int get_gmtoff(const struct tm *tp)
#endif
}
-static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf, size_t bufsz)
+static int format_iso_time(const struct tm *tm, uint32_t nsec, int flags, char *buf, size_t bufsz)
{
+ uint32_t usec = nsec / NSEC_PER_USEC;
char *p = buf;
int len;
@@ -479,15 +499,28 @@ static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf
p += len;
}
- if (flags & ISO_DOTUSEC) {
- len = snprintf(p, bufsz, ".%06"PRId64, (int64_t) usec);
+ if (flags & ISO_DOTNSEC) {
+ len = snprintf(p, bufsz, ".%09"PRIu32, nsec);
+ if (len < 0 || (size_t) len > bufsz)
+ goto err;
+ bufsz -= len;
+ p += len;
+
+ } else if (flags & ISO_COMMANSEC) {
+ len = snprintf(p, bufsz, ",%09"PRIu32, nsec);
+ if (len < 0 || (size_t) len > bufsz)
+ goto err;
+ bufsz -= len;
+ p += len;
+ } else if (flags & ISO_DOTUSEC) {
+ len = snprintf(p, bufsz, ".%06"PRIu32, usec);
if (len < 0 || (size_t) len > bufsz)
goto err;
bufsz -= len;
p += len;
} else if (flags & ISO_COMMAUSEC) {
- len = snprintf(p, bufsz, ",%06"PRId64, (int64_t) usec);
+ len = snprintf(p, bufsz, ",%06"PRIu32, usec);
if (len < 0 || (size_t) len > bufsz)
goto err;
bufsz -= len;
@@ -508,26 +541,37 @@ static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf
return -1;
}
-/* timeval to ISO 8601 */
-int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz)
+/* timespec to ISO 8601 */
+int strtimespec_iso(const struct timespec *ts, int flags, char *buf, size_t bufsz)
{
struct tm tm;
struct tm *rc;
if (flags & ISO_GMTIME)
- rc = gmtime_r(&tv->tv_sec, &tm);
+ rc = gmtime_r(&ts->tv_sec, &tm);
else
- rc = localtime_r(&tv->tv_sec, &tm);
+ rc = localtime_r(&ts->tv_sec, &tm);
if (rc)
- return format_iso_time(&tm, tv->tv_usec, flags, buf, bufsz);
+ return format_iso_time(&tm, ts->tv_nsec, flags, buf, bufsz);
- warnx(_("time %"PRId64" is out of range."), (int64_t)(tv->tv_sec));
+ warnx(_("time %"PRId64" is out of range."), (int64_t)(ts->tv_sec));
return -1;
}
+/* timeval to ISO 8601 */
+int strtimeval_iso(const struct timeval *tv, int flags, char *buf, size_t bufsz)
+{
+ struct timespec ts = {
+ .tv_sec = tv->tv_sec,
+ .tv_nsec = tv->tv_usec * NSEC_PER_USEC,
+ };
+
+ return strtimespec_iso(&ts, flags, buf, bufsz);
+}
+
/* struct tm to ISO 8601 */
-int strtm_iso(struct tm *tm, int flags, char *buf, size_t bufsz)
+int strtm_iso(const struct tm *tm, int flags, char *buf, size_t bufsz)
{
return format_iso_time(tm, 0, flags, buf, bufsz);
}
@@ -592,6 +636,63 @@ int strtime_short(const time_t *t, struct timeval *now, int flags, char *buf, si
return rc <= 0 ? -1 : 0;
}
+int strtimespec_relative(const struct timespec *ts, char *buf, size_t bufsz)
+{
+ time_t secs = ts->tv_sec;
+ size_t i, parts = 0;
+ int rc;
+
+ if (bufsz)
+ buf[0] = '\0';
+
+ static const struct {
+ const char * const suffix;
+ int width;
+ int64_t secs;
+ } table[] = {
+ { "y", 4, NSEC_PER_YEAR / NSEC_PER_SEC },
+ { "d", 3, NSEC_PER_DAY / NSEC_PER_SEC },
+ { "h", 2, NSEC_PER_HOUR / NSEC_PER_SEC },
+ { "m", 2, NSEC_PER_MINUTE / NSEC_PER_SEC },
+ { "s", 2, NSEC_PER_SEC / NSEC_PER_SEC },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(table); i++) {
+ if (secs >= table[i].secs) {
+ rc = snprintf(buf, bufsz,
+ "%*"PRId64"%s%s",
+ parts ? table[i].width : 0,
+ secs / table[i].secs, table[i].suffix,
+ secs % table[i].secs ? " " : "");
+ if (rc < 0 || (size_t) rc > bufsz)
+ goto err;
+ parts++;
+ buf += rc;
+ bufsz -= rc;
+ secs %= table[i].secs;
+ }
+ }
+
+ if (ts->tv_nsec) {
+ if (ts->tv_nsec % NSEC_PER_MSEC) {
+ rc = snprintf(buf, bufsz, "%*luns",
+ parts ? 10 : 0, ts->tv_nsec);
+ if (rc < 0 || (size_t) rc > bufsz)
+ goto err;
+ } else {
+ rc = snprintf(buf, bufsz, "%*llums",
+ parts ? 4 : 0, ts->tv_nsec / NSEC_PER_MSEC);
+ if (rc < 0 || (size_t) rc > bufsz)
+ goto err;
+ }
+ }
+
+ return 0;
+ err:
+ warnx(_("format_reltime: buffer overflow."));
+ return -1;
+}
+
#ifndef HAVE_TIMEGM
time_t timegm(struct tm *tm)
{
@@ -625,6 +726,7 @@ static int run_unittest_timestamp(void)
{ "2012-09-22 16:34:22.012", 1348331662012000 },
{ "@1348331662" , 1348331662000000 },
{ "@1348331662.234567" , 1348331662234567 },
+ { "@0" , 0 },
{ "2012-09-22 16:34" , 1348331640000000 },
{ "2012-09-22" , 1348272000000000 },
{ "16:34:22" , 1674232462000000 },
@@ -662,45 +764,142 @@ static int run_unittest_timestamp(void)
return rc;
}
+static int run_unittest_format(void)
+{
+ int rc = EXIT_SUCCESS;
+ const struct timespec ts = {
+ .tv_sec = 1674180427,
+ .tv_nsec = 12345,
+ };
+ char buf[FORMAT_TIMESTAMP_MAX];
+ static const struct testcase {
+ int flags;
+ const char * const expected;
+ } testcases[] = {
+ { ISO_DATE, "2023-01-20" },
+ { ISO_TIME, "02:07:07" },
+ { ISO_TIMEZONE, "+00:00" },
+ { ISO_TIMESTAMP_T, "2023-01-20T02:07:07+00:00" },
+ { ISO_TIMESTAMP_COMMA_G, "2023-01-20 02:07:07,000012+00:00" },
+ { ISO_TIME | ISO_DOTNSEC, "02:07:07.000012345" },
+ };
+
+ setenv("TZ", "GMT", 1);
+ tzset();
+
+ for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
+ struct testcase t = testcases[i];
+ int r = strtimespec_iso(&ts, t.flags, buf, sizeof(buf));
+ if (r) {
+ fprintf(stderr, "Could not format '%s'\n", t.expected);
+ rc = EXIT_FAILURE;
+ }
+
+ if (strcmp(buf, t.expected)) {
+ fprintf(stderr, "#%02zu %-20s != %-20s\n", i, buf, t.expected);
+ rc = EXIT_FAILURE;
+ }
+ }
+
+ return rc;
+}
+
+static int run_unittest_format_relative(void)
+{
+ int rc = EXIT_SUCCESS;
+ char buf[FORMAT_TIMESTAMP_MAX];
+ static const struct testcase {
+ struct timespec ts;
+ const char * const expected;
+ } testcases[] = {
+ {{}, "" },
+ {{ 1 }, "1s" },
+ {{ 10 }, "10s" },
+ {{ 100 }, "1m 40s" },
+ {{ 1000 }, "16m 40s" },
+ {{ 10000 }, "2h 46m 40s" },
+ {{ 100000 }, "1d 3h 46m 40s" },
+ {{ 1000000 }, "11d 13h 46m 40s" },
+ {{ 10000000 }, "115d 17h 46m 40s" },
+ {{ 100000000 }, "3y 61d 15h 46m 40s" },
+ {{ 60 }, "1m" },
+ {{ 3600 }, "1h" },
+
+ {{ 1, 1 }, "1s 1ns" },
+ {{ 0, 1 }, "1ns" },
+ {{ 0, 1000000 }, "1ms" },
+ {{ 0, 1000001 }, "1000001ns" },
+ };
+
+ setenv("TZ", "GMT", 1);
+ tzset();
+
+ for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
+ struct testcase t = testcases[i];
+ int r = strtimespec_relative(&t.ts, buf, sizeof(buf));
+ if (r) {
+ fprintf(stderr, "Could not format '%s'\n", t.expected);
+ rc = EXIT_FAILURE;
+ }
+
+ if (strcmp(buf, t.expected)) {
+ fprintf(stderr, "#%02zu '%-20s' != '%-20s'\n", i, buf, t.expected);
+ rc = EXIT_FAILURE;
+ }
+ }
+
+ return rc;
+}
+
int main(int argc, char *argv[])
{
- struct timeval tv = { 0 };
+ struct timespec ts = { 0 };
char buf[ISO_BUFSIZ];
+ int r;
if (argc < 2) {
fprintf(stderr, "usage: %s [<time> [<usec>]] | [--timestamp <str>] | [--unittest-timestamp]\n", argv[0]);
exit(EXIT_FAILURE);
}
- if (strcmp(argv[1], "--unittest-timestamp") == 0) {
+ if (strcmp(argv[1], "--unittest-timestamp") == 0)
return run_unittest_timestamp();
- }
+ else if (strcmp(argv[1], "--unittest-format") == 0)
+ return run_unittest_format();
+ else if (strcmp(argv[1], "--unittest-format-relative") == 0)
+ return run_unittest_format_relative();
if (strcmp(argv[1], "--timestamp") == 0) {
usec_t usec = 0;
- parse_timestamp(argv[2], &usec);
- tv.tv_sec = (time_t) (usec / 1000000);
- tv.tv_usec = usec % 1000000;
+ r = parse_timestamp(argv[2], &usec);
+ if (r)
+ errx(EXIT_FAILURE, "Can not parse '%s': %s", argv[2], strerror(-r));
+ ts.tv_sec = (time_t) (usec / USEC_PER_SEC);
+ ts.tv_nsec = (usec % USEC_PER_SEC) * NSEC_PER_USEC;
} else {
- tv.tv_sec = strtos64_or_err(argv[1], "failed to parse <time>");
+ ts.tv_sec = strtos64_or_err(argv[1], "failed to parse <time>");
if (argc == 3)
- tv.tv_usec = strtos64_or_err(argv[2], "failed to parse <usec>");
+ ts.tv_nsec = strtos64_or_err(argv[2], "failed to parse <usec>")
+ * NSEC_PER_USEC;
}
- strtimeval_iso(&tv, ISO_DATE, buf, sizeof(buf));
+ strtimespec_iso(&ts, ISO_DATE, buf, sizeof(buf));
printf("Date: '%s'\n", buf);
- strtimeval_iso(&tv, ISO_TIME, buf, sizeof(buf));
+ strtimespec_iso(&ts, ISO_TIME, buf, sizeof(buf));
printf("Time: '%s'\n", buf);
- strtimeval_iso(&tv, ISO_DATE | ISO_TIME | ISO_COMMAUSEC | ISO_T,
+ strtimespec_iso(&ts, ISO_DATE | ISO_TIME | ISO_COMMAUSEC | ISO_T,
buf, sizeof(buf));
printf("Full: '%s'\n", buf);
- strtimeval_iso(&tv, ISO_TIMESTAMP_DOT, buf, sizeof(buf));
+ strtimespec_iso(&ts, ISO_TIMESTAMP_DOT, buf, sizeof(buf));
printf("Zone: '%s'\n", buf);
+ strtimespec_relative(&ts, buf, sizeof(buf));
+ printf("Rel: '%s'\n", buf);
+
return EXIT_SUCCESS;
}
diff --git a/lib/ttyutils.c b/lib/ttyutils.c
index 7064565..bacd730 100644
--- a/lib/ttyutils.c
+++ b/lib/ttyutils.c
@@ -7,9 +7,40 @@
#include <ctype.h>
#include <unistd.h>
+#ifdef HAVE_GETTTYNAM
+# include <ttyent.h>
+#endif
+
#include "c.h"
#include "ttyutils.h"
+#ifdef __linux__
+# 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
+#elif defined(__GNU__)
+# ifndef DEFAULT_VCTERM
+# define DEFAULT_VCTERM "hurd"
+# endif
+# ifndef DEFAULT_STERM
+# define DEFAULT_STERM "vt102"
+# endif
+#else
+# ifndef DEFAULT_VCTERM
+# define DEFAULT_VCTERM "vt100"
+# endif
+# ifndef DEFAULT_STERM
+# define DEFAULT_STERM "vt100"
+# endif
+#endif
static int get_env_int(const char *name)
{
@@ -130,6 +161,35 @@ int get_terminal_type(const char **type)
return 0;
}
+char *get_terminal_default_type(const char *ttyname, int is_serial)
+{
+ if (ttyname) {
+#ifdef HAVE_GETTTYNAM
+ struct ttyent *ent = getttynam(ttyname);
+
+ if (ent && ent->ty_type)
+ return strdup(ent->ty_type);
+#endif
+
+#if defined (__s390__) || defined (__s390x__)
+ /*
+ * 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(ttyname, "ttyS0") == 0) /* linux/drivers/s390/char/con3215.c */
+ return strdup(DEFAULT_TTYS0);
+ else if (strncmp(ttyname, "3270/tty", 8) == 0) /* linux/drivers/s390/char/con3270.c */
+ return strdup(DEFAULT_TTY32);
+ else if (strcmp(ttyname, "ttyS1") == 0) /* linux/drivers/s390/char/sclp_vt220.c */
+ return strdup(DEFAULT_TTYS1);
+#endif
+ }
+
+ return strdup(is_serial ? DEFAULT_STERM : DEFAULT_VCTERM);
+}
+
#ifdef TEST_PROGRAM_TTYUTILS
# include <stdlib.h>
int main(void)
@@ -138,13 +198,21 @@ int main(void)
int c, l;
if (get_terminal_name(&path, &name, &num) == 0) {
- fprintf(stderr, "tty path: %s\n", path);
- fprintf(stderr, "tty name: %s\n", name);
- fprintf(stderr, "tty number: %s\n", num);
+ char *term;
+
+ fprintf(stderr, "tty path: %s\n", path);
+ fprintf(stderr, "tty name: %s\n", name);
+ fprintf(stderr, "tty number: %s\n", num);
+
+ fprintf(stderr, "tty term: %s\n", getenv("TERM"));
+
+ term = get_terminal_default_type(name, 0);
+ fprintf(stderr, "tty dflt term: %s\n", term);
+ free(term);
}
get_terminal_dimension(&c, &l);
- fprintf(stderr, "tty cols: %d\n", c);
- fprintf(stderr, "tty lines: %d\n", l);
+ fprintf(stderr, "tty cols: %d\n", c);
+ fprintf(stderr, "tty lines: %d\n", l);
return EXIT_SUCCESS;