diff options
author | Manuel Bentele | 2021-12-07 10:17:15 +0100 |
---|---|---|
committer | Manuel Bentele | 2021-12-07 10:17:15 +0100 |
commit | 424839be05e2f4c5d4684ab0c37e00c2881672bd (patch) | |
tree | b6fa21c4f6f5a64782f941c4eaf4ae67c7995f89 /src | |
parent | Fix issue in CMake kernel header check (diff) | |
download | xloop-424839be05e2f4c5d4684ab0c37e00c2881672bd.tar.gz xloop-424839be05e2f4c5d4684ab0c37e00c2881672bd.tar.xz xloop-424839be05e2f4c5d4684ab0c37e00c2881672bd.zip |
Update xlosetup from util-linux v2.37.2
Diffstat (limited to 'src')
33 files changed, 1242 insertions, 625 deletions
diff --git a/src/utils/include/c.h b/src/utils/include/c.h index ae08131..354b59e 100644 --- a/src/utils/include/c.h +++ b/src/utils/include/c.h @@ -16,6 +16,8 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <sys/types.h> +#include <grp.h> #include <assert.h> @@ -64,6 +66,21 @@ # define ignore_result(x) ((void) (x)) #endif /* !__GNUC__ */ + +/* "restrict" keyword fallback */ +#if __STDC__ != 1 +# define restrict __restrict /* use implementation __ format */ +#else +# ifndef __STDC_VERSION__ +# define restrict __restrict /* use implementation __ format */ +# else +# if __STDC_VERSION__ < 199901L +# define restrict __restrict /* use implementation __ format */ +# endif +# endif +#endif + + /* * It evaluates to 1 if the attribute/feature is supported by the current * compilation target. Fallback for old compilers. @@ -202,7 +219,7 @@ prog_inv_sh_nm_from_file(char *f, char stripext) #ifndef HAVE_ERR_H -static inline void +static inline void __attribute__ ((__format__ (__printf__, 4, 5))) errmsg(char doexit, int excode, char adderr, const char *fmt, ...) { fprintf(stderr, "%s: ", program_invocation_short_name); @@ -320,6 +337,24 @@ static inline size_t get_hostname_max(void) return 64; } + +static inline int drop_permissions(void) +{ + errno = 0; + + /* drop GID */ + if (setgid(getgid()) < 0) + goto fail; + + /* drop UID */ + if (setuid(getuid()) < 0) + goto fail; + + return 0; +fail: + return errno ? -errno : -1; +} + /* * The usleep function was marked obsolete in POSIX.1-2001 and was removed * in POSIX.1-2008. It was replaced with nanosleep() that provides more @@ -396,6 +431,23 @@ static inline int xusleep(useconds_t usec) #define stringify_value(s) stringify(s) #define stringify(s) #s +/* Detect if we're compiled with Address Sanitizer + * - gcc (__SANITIZE_ADDRESS__) + * - clang (__has_feature(address_sanitizer)) + */ +#if !defined(HAS_FEATURE_ADDRESS_SANITIZER) +# ifdef __SANITIZE_ADDRESS__ +# define HAS_FEATURE_ADDRESS_SANITIZER 1 +# elif defined(__has_feature) +# if __has_feature(address_sanitizer) +# define HAS_FEATURE_ADDRESS_SANITIZER 1 +# endif +# endif +# if !defined(HAS_FEATURE_ADDRESS_SANITIZER) +# define HAS_FEATURE_ADDRESS_SANITIZER 0 +# endif +#endif + /* * UL_ASAN_BLACKLIST is a macro to tell AddressSanitizer (a compile-time * instrumentation shipped with Clang and GCC) to not instrument the diff --git a/src/utils/include/carefulputc.h b/src/utils/include/carefulputc.h index f1c0356..66a0f15 100644 --- a/src/utils/include/carefulputc.h +++ b/src/utils/include/carefulputc.h @@ -28,82 +28,6 @@ static inline int fputc_careful(int c, FILE *fp, const char fail) return (ret < 0) ? EOF : 0; } -/* - * Requirements enumerated via testing (V8, Firefox, IE11): - * - * var charsToEscape = []; - * for (var i = 0; i < 65535; i += 1) { - * try { - * JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}'); - * } catch (e) { - * charsToEscape.push(i); - * } - * } - */ -static inline void fputs_quoted_case_json(const char *data, FILE *out, int dir) -{ - const char *p; - - fputc('"', out); - for (p = data; p && *p; p++) { - - const unsigned char c = (unsigned char) *p; - - /* From http://www.json.org - * - * The double-quote and backslashes would break out a string or - * init an escape sequence if not escaped. - * - * Note that single-quotes and forward slashes, while they're - * in the JSON spec, don't break double-quoted strings. - */ - if (c == '"' || c == '\\') { - fputc('\\', out); - fputc(c, out); - continue; - } - - /* All non-control characters OK; do the case swap as required. */ - if (c >= 0x20) { - fputc(dir == 1 ? toupper(c) : - dir == -1 ? tolower(c) : *p, out); - continue; - } - - /* In addition, all chars under ' ' break Node's/V8/Chrome's, and - * Firefox's JSON.parse function - */ - switch (c) { - /* Handle short-hand cases to reduce output size. C - * has most of the same stuff here, so if there's an - * "Escape for C" function somewhere in the STL, we - * should probably be using it. - */ - case '\b': - fputs("\\b", out); - break; - case '\t': - fputs("\\t", out); - break; - case '\n': - fputs("\\n", out); - break; - case '\f': - fputs("\\f", out); - break; - case '\r': - fputs("\\r", out); - break; - default: - /* Other assorted control characters */ - fprintf(out, "\\u00%02x", c); - break; - } - } - fputc('"', out); -} - - static inline void fputs_quoted_case(const char *data, FILE *out, int dir) { const char *p; @@ -130,10 +54,6 @@ static inline void fputs_quoted_case(const char *data, FILE *out, int dir) #define fputs_quoted_upper(_d, _o) fputs_quoted_case(_d, _o, 1) #define fputs_quoted_lower(_d, _o) fputs_quoted_case(_d, _o, -1) -#define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0) -#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1) -#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1) - static inline void fputs_nonblank(const char *data, FILE *out) { const char *p; @@ -151,5 +71,21 @@ static inline void fputs_nonblank(const char *data, FILE *out) } } +static inline void fputs_shell_ident(const char *data, FILE *out) +{ + const char *p = data; + + /* convert "1FOO" to "_1FOO" */ + if (p && !isalpha(*p)) + fputc('_', out); + + /* replace all "bad" chars with "_" */ + for (p = data; p && *p; p++) { + if (!isalnum(*p)) + fputc('_', out); + else + fputc(*p, out); + } +} #endif /* _CAREFULPUTC_H */ diff --git a/src/utils/include/closestream.h b/src/utils/include/closestream.h index 41afbe2..8e5d18d 100644 --- a/src/utils/include/closestream.h +++ b/src/utils/include/closestream.h @@ -83,7 +83,7 @@ close_stdout_atexit(void) /* * Note that close stdout at exit disables ASAN to report memory leaks */ -#if !defined(__SANITIZE_ADDRESS__) +#if !HAS_FEATURE_ADDRESS_SANITIZER atexit(close_stdout); #endif } diff --git a/src/utils/include/fileutils.h b/src/utils/include/fileutils.h index 618bf39..17ad429 100644 --- a/src/utils/include/fileutils.h +++ b/src/utils/include/fileutils.h @@ -34,10 +34,15 @@ static inline FILE *fopen_at(int dir, const char *filename, int flags, const char *mode) { int fd = openat(dir, filename, flags); + FILE *ret; + if (fd < 0) return NULL; - return fdopen(fd, mode); + ret = fdopen(fd, mode); + if (!ret) + close(fd); + return ret; } #endif @@ -53,9 +58,9 @@ static inline int is_same_inode(const int fd, const struct stat *st) } extern int dup_fd_cloexec(int oldfd, int lowfd); -extern int get_fd_tabsize(void); +extern unsigned int get_fd_tabsize(void); -extern int mkdir_p(const char *path, mode_t mode); +extern int ul_mkdir_p(const char *path, mode_t mode); extern char *stripoff_last_component(char *path); /* This is readdir()-like function, but skips "." and ".." directory entries */ @@ -72,7 +77,21 @@ static inline struct dirent *xreaddir(DIR *dp) return d; } -extern void close_all_fds(const int exclude[], size_t exsz); +#if defined(__linux__) +# include <sys/syscall.h> +# if defined(SYS_close_range) +# include <sys/types.h> +# ifndef HAVE_CLOSE_RANGE +static inline int close_range(unsigned int first, unsigned int last, int flags) +{ + return syscall(SYS_close_range, first, last, flags); +} +# endif +# define HAVE_CLOSE_RANGE 1 +# endif /* SYS_close_range */ +#endif /* __linux__ */ + +extern void ul_close_all_fds(unsigned int first, unsigned int last); #define UL_COPY_READ_ERROR (-1) #define UL_COPY_WRITE_ERROR (-2) diff --git a/src/utils/include/jsonwrt.h b/src/utils/include/jsonwrt.h index 04ef49e..5be2d70 100644 --- a/src/utils/include/jsonwrt.h +++ b/src/utils/include/jsonwrt.h @@ -11,34 +11,34 @@ struct ul_jsonwrt { FILE *out; int indent; - unsigned int postponed_break :1; + unsigned int after_close :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); +void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type); #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_root_close(_f) ul_jsonwrt_close(_f, UL_JSON_OBJECT) #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_array_close(_f) ul_jsonwrt_close(_f, UL_JSON_ARRAY) #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_object_close(_f) ul_jsonwrt_close(_f, UL_JSON_OBJECT) #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) +#define ul_jsonwrt_value_close(_f) ul_jsonwrt_close(_f, UL_JSON_VALUE) void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt, - const char *name, const char *data, int islast); + const char *name, const char *data); void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt, - const char *name, const char *data, int islast); + const char *name, const char *data); void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt, - const char *name, uint64_t data, int islast); + const char *name, uint64_t data); void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt, - const char *name, int data, int islast); + const char *name, int data); #endif /* UTIL_LINUX_JSONWRT_H */ diff --git a/src/utils/include/path.h b/src/utils/include/path.h index 2a4f80e..e720893 100644 --- a/src/utils/include/path.h +++ b/src/utils/include/path.h @@ -23,7 +23,8 @@ struct path_cxt { int (*redirect_on_enoent)(struct path_cxt *, const char *, int *); }; -struct path_cxt *ul_new_path(const char *dir, ...); +struct path_cxt *ul_new_path(const char *dir, ...) + __attribute__ ((__format__ (__printf__, 1, 2))); void ul_unref_path(struct path_cxt *pc); void ul_ref_path(struct path_cxt *pc); @@ -55,15 +56,18 @@ int ul_path_accessf(struct path_cxt *pc, int mode, const char *path, ...) int ul_path_open(struct path_cxt *pc, int flags, const char *path); int ul_path_openf(struct path_cxt *pc, int flags, const char *path, ...) __attribute__ ((__format__ (__printf__, 3, 4))); -int ul_path_vopenf(struct path_cxt *pc, int flags, const char *path, va_list ap); +int ul_path_vopenf(struct path_cxt *pc, int flags, const char *path, va_list ap) + __attribute__ ((__format__ (__printf__, 3, 0))); FILE *ul_path_fopen(struct path_cxt *pc, const char *mode, const char *path); FILE *ul_path_fopenf(struct path_cxt *pc, const char *mode, const char *path, ...) __attribute__ ((__format__ (__printf__, 3, 4))); -FILE *ul_path_vfopenf(struct path_cxt *pc, const char *mode, const char *path, va_list ap); +FILE *ul_path_vfopenf(struct path_cxt *pc, const char *mode, const char *path, va_list ap) + __attribute__ ((__format__ (__printf__, 3, 0))); DIR *ul_path_opendir(struct path_cxt *pc, const char *path); -DIR *ul_path_vopendirf(struct path_cxt *pc, const char *path, va_list ap); +DIR *ul_path_vopendirf(struct path_cxt *pc, const char *path, va_list ap) + __attribute__ ((__format__ (__printf__, 2, 0))); DIR *ul_path_opendirf(struct path_cxt *pc, const char *path, ...) __attribute__ ((__format__ (__printf__, 2, 3))); @@ -72,7 +76,8 @@ ssize_t ul_path_readlinkf(struct path_cxt *pc, char *buf, size_t bufsiz, const c __attribute__ ((__format__ (__printf__, 4, 5))); int ul_path_read(struct path_cxt *pc, char *buf, size_t len, const char *path); -int ul_path_vreadf(struct path_cxt *pc, char *buf, size_t len, const char *path, va_list ap); +int ul_path_vreadf(struct path_cxt *pc, char *buf, size_t len, const char *path, va_list ap) + __attribute__ ((__format__ (__printf__, 4, 0))); int ul_path_readf(struct path_cxt *pc, char *buf, size_t len, const char *path, ...) __attribute__ ((__format__ (__printf__, 4, 5))); @@ -84,8 +89,10 @@ int ul_path_read_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char int ul_path_readf_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, ...) __attribute__ ((__format__ (__printf__, 4, 5))); -int ul_path_scanf(struct path_cxt *pc, const char *path, const char *fmt, ...); +int ul_path_scanf(struct path_cxt *pc, const char *path, const char *fmt, ...) + __attribute__ ((__format__ (__scanf__, 3, 4))); int ul_path_scanff(struct path_cxt *pc, const char *path, va_list ap, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 0))) __attribute__ ((__format__ (__scanf__, 4, 5))); int ul_path_read_majmin(struct path_cxt *pc, dev_t *res, const char *path); diff --git a/src/utils/include/pathnames.h b/src/utils/include/pathnames.h index 8f1bb56..9be2baa 100644 --- a/src/utils/include/pathnames.h +++ b/src/utils/include/pathnames.h @@ -144,8 +144,8 @@ #define _PATH_DEV_MEM "/dev/mem" -#define _PATH_DEV_LOOP "/dev/xloop" -#define _PATH_DEV_LOOPCTL "/dev/xloop-control" +#define _PATH_DEV_LOOP "/dev/loop" +#define _PATH_DEV_LOOPCTL "/dev/loop-control" /* udev paths */ #define _PATH_DEV_BYLABEL "/dev/disk/by-label" @@ -174,17 +174,23 @@ /* deprecated */ #define _PATH_RAWDEVCTL_OLD "/dev/rawctl" +#define _PATH_PROC_KERNEL "/proc/sys/kernel" + /* ipc paths */ #define _PATH_PROC_SYSV_MSG "/proc/sysvipc/msg" #define _PATH_PROC_SYSV_SEM "/proc/sysvipc/sem" #define _PATH_PROC_SYSV_SHM "/proc/sysvipc/shm" -#define _PATH_PROC_IPC_MSGMAX "/proc/sys/kernel/msgmax" -#define _PATH_PROC_IPC_MSGMNB "/proc/sys/kernel/msgmnb" -#define _PATH_PROC_IPC_MSGMNI "/proc/sys/kernel/msgmni" -#define _PATH_PROC_IPC_SEM "/proc/sys/kernel/sem" -#define _PATH_PROC_IPC_SHMALL "/proc/sys/kernel/shmall" -#define _PATH_PROC_IPC_SHMMAX "/proc/sys/kernel/shmmax" -#define _PATH_PROC_IPC_SHMMNI "/proc/sys/kernel/shmmni" +#define _PATH_PROC_IPC_MSGMAX _PATH_PROC_KERNEL "/msgmax" +#define _PATH_PROC_IPC_MSGMNB _PATH_PROC_KERNEL "/msgmnb" +#define _PATH_PROC_IPC_MSGMNI _PATH_PROC_KERNEL "/msgmni" +#define _PATH_PROC_IPC_SEM _PATH_PROC_KERNEL "/sem" +#define _PATH_PROC_IPC_SHMALL _PATH_PROC_KERNEL "/shmall" +#define _PATH_PROC_IPC_SHMMAX _PATH_PROC_KERNEL "/shmmax" +#define _PATH_PROC_IPC_SHMMNI _PATH_PROC_KERNEL "/shmmni" + +/* util clamp */ +#define _PATH_PROC_UCLAMP_MIN _PATH_PROC_KERNEL "/sched_util_clamp_min" +#define _PATH_PROC_UCLAMP_MAX _PATH_PROC_KERNEL "/sched_util_clamp_max" /* irqtop paths */ #define _PATH_PROC_INTERRUPTS "/proc/interrupts" @@ -194,6 +200,7 @@ /* kernel command line */ #define _PATH_PROC_CMDLINE "/proc/cmdline" + /* logger paths */ #define _PATH_DEVLOG "/dev/log" @@ -207,4 +214,5 @@ #define _PATH_DEV_RFKILL "/dev/rfkill" #define _PATH_SYS_RFKILL "/sys/class/rfkill" + #endif /* PATHNAMES_H */ diff --git a/src/utils/include/pty-session.h b/src/utils/include/pty-session.h index 0c9ccc6..09eff43 100644 --- a/src/utils/include/pty-session.h +++ b/src/utils/include/pty-session.h @@ -10,6 +10,7 @@ #include <termios.h> #include <signal.h> #include <sys/time.h> +#include <sys/stat.h> #include <sys/signalfd.h> @@ -98,6 +99,7 @@ struct ul_pty_callbacks *ul_pty_get_callbacks(struct ul_pty *pty); int ul_pty_is_running(struct ul_pty *pty); int ul_pty_setup(struct ul_pty *pty); void ul_pty_cleanup(struct ul_pty *pty); +int ul_pty_chownmod_slave(struct ul_pty *pty, uid_t uid, gid_t gid, mode_t mode); void ul_pty_init_slave(struct ul_pty *pty); int ul_pty_proxy_master(struct ul_pty *pty); diff --git a/src/utils/include/strutils.h b/src/utils/include/strutils.h index 09cc35e..6e95707 100644 --- a/src/utils/include/strutils.h +++ b/src/utils/include/strutils.h @@ -8,6 +8,7 @@ #include <ctype.h> #include <stdio.h> #include <errno.h> +#include <time.h> #include "c.h" @@ -18,25 +19,35 @@ extern int parse_size(const char *str, uintmax_t *res, int *power); extern int strtosize(const char *str, uintmax_t *res); extern uintmax_t strtosize_or_err(const char *str, const char *errmesg); -extern int16_t strtos16_or_err(const char *str, const char *errmesg); -extern uint16_t strtou16_or_err(const char *str, const char *errmesg); -extern uint16_t strtox16_or_err(const char *str, const char *errmesg); +extern int ul_strtos64(const char *str, int64_t *num, int base); +extern int ul_strtou64(const char *str, uint64_t *num, int base); +extern int ul_strtos32(const char *str, int32_t *num, int base); +extern int ul_strtou32(const char *str, uint32_t *num, int base); -extern int32_t strtos32_or_err(const char *str, const char *errmesg); -extern uint32_t strtou32_or_err(const char *str, const char *errmesg); -extern uint32_t strtox32_or_err(const char *str, const char *errmesg); +extern int64_t str2num_or_err(const char *str, int base, const char *errmesg, int64_t low, int64_t up); +extern uint64_t str2unum_or_err(const char *str, int base, const char *errmesg, uint64_t up); -extern int64_t strtos64_or_err(const char *str, const char *errmesg); -extern uint64_t strtou64_or_err(const char *str, const char *errmesg); -extern uint64_t strtox64_or_err(const char *str, const char *errmesg); +#define strtos64_or_err(_s, _e) str2num_or_err(_s, 10, _e, 0, 0) +#define strtou64_or_err(_s, _e) str2unum_or_err(_s, 10, _e, 0) +#define strtox64_or_err(_s, _e) str2unum_or_err(_s, 16, _e, 0) + +#define strtos32_or_err(_s, _e) (int32_t) str2num_or_err(_s, 10, _e, INT32_MIN, INT32_MAX) +#define strtou32_or_err(_s, _e) (uint32_t) str2unum_or_err(_s, 10, _e, UINT32_MAX) +#define strtox32_or_err(_s, _e) (uint32_t) str2unum_or_err(_s, 16, _e, UINT32_MAX) + +#define strtos16_or_err(_s, _e) (int16_t) str2num_or_err(_s, 10, _e, INT16_MIN, INT16_MAX) +#define strtou16_or_err(_s, _e) (uint16_t) str2unum_or_err(_s, 10, _e, UINT16_MAX) +#define strtox16_or_err(_s, _e) (uint16_t) str2unum_or_err(_s, 16, _e, UINT16_MAX) extern double strtod_or_err(const char *str, const char *errmesg); +extern long double strtold_or_err(const char *str, const char *errmesg); extern long strtol_or_err(const char *str, const char *errmesg); extern unsigned long strtoul_or_err(const char *str, const char *errmesg); extern void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg); +extern time_t strtotime_or_err(const char *str, const char *errmesg); extern int isdigit_strend(const char *str, const char **end); #define isdigit_string(_s) isdigit_strend(_s, NULL) @@ -331,7 +342,7 @@ static inline size_t normalize_whitespace(unsigned char *str) else str[x++] = str[i++]; } - if (nsp) /* tailing space */ + if (nsp && x > 0) /* tailing space */ x--; str[x] = '\0'; return x; @@ -359,9 +370,10 @@ static inline void strrem(char *s, int rem) extern char *strnappend(const char *s, const char *suffix, size_t b); extern char *strappend(const char *s, const char *suffix); extern char *strfappend(const char *s, const char *format, ...) - __attribute__ ((__format__ (__printf__, 2, 0))); + __attribute__ ((__format__ (__printf__, 2, 3))); extern const char *split(const char **state, size_t *l, const char *separator, int quoted); extern int skip_fline(FILE *fp); +extern int ul_stralnumcmp(const char *p1, const char *p2); #endif diff --git a/src/utils/include/strv.h b/src/utils/include/strv.h index 260ad12..6382532 100644 --- a/src/utils/include/strv.h +++ b/src/utils/include/strv.h @@ -13,9 +13,12 @@ unsigned strv_length(char * const *l); int strv_extend_strv(char ***a, char **b); int strv_extend_strv_concat(char ***a, char **b, const char *suffix); int strv_extend(char ***l, const char *value); -int strv_extendv(char ***l, const char *format, va_list ap); + +int strv_extendv(char ***l, const char *format, va_list ap) + __attribute__ ((__format__ (__printf__, 2, 0))); int strv_extendf(char ***l, const char *format, ...) - __attribute__ ((__format__ (__printf__, 2, 0))); + __attribute__ ((__format__ (__printf__, 2, 3))); + int strv_push(char ***l, char *value); int strv_push_prepend(char ***l, char *value); int strv_consume(char ***l, char *value); diff --git a/src/utils/include/ttyutils.h b/src/utils/include/ttyutils.h index f164a58..e28a2b0 100644 --- a/src/utils/include/ttyutils.h +++ b/src/utils/include/ttyutils.h @@ -17,6 +17,13 @@ #include <sys/ttydefaults.h> #endif +#ifdef USE_TTY_GROUP +# define TTY_MODE 0620 +#else +# define TTY_MODE 0600 +#endif +#define TTYGRPNAME "tty" /* name of group to own ttys */ + /* Some shorthands for control characters. */ #define CTL(x) ((x) ^ 0100) /* Assumes ASCII dialect */ #define CR CTL('M') /* carriage return */ @@ -73,7 +80,7 @@ struct chardata { #define INIT_CHARDATA(ptr) do { \ (ptr)->erase = DEF_ERASE; \ (ptr)->kill = DEF_KILL; \ - (ptr)->eol = CTRL('r'); \ + (ptr)->eol = CR; \ (ptr)->parity = 0; \ (ptr)->capslock = 0; \ } while (0) diff --git a/src/utils/lib/canonicalize.c b/src/utils/lib/canonicalize.c index e101c5b..6f85b47 100644 --- a/src/utils/lib/canonicalize.c +++ b/src/utils/lib/canonicalize.c @@ -170,8 +170,7 @@ char *canonicalize_path_restricted(const char *path) pipes[0] = -1; errno = 0; - /* drop permissions */ - if (setgid(getgid()) < 0 || setuid(getuid()) < 0) + if (drop_permissions() != 0) canonical = NULL; /* failed */ else { char *dmname = NULL; diff --git a/src/utils/lib/fileutils.c b/src/utils/lib/fileutils.c index 9da906a..7a8fce2 100644 --- a/src/utils/lib/fileutils.c +++ b/src/utils/lib/fileutils.c @@ -110,7 +110,7 @@ unwind: /* * portable getdtablesize() */ -int get_fd_tabsize(void) +unsigned int get_fd_tabsize(void) { int m; @@ -129,18 +129,7 @@ int get_fd_tabsize(void) return m; } -static inline int in_set(int x, const int set[], size_t setsz) -{ - size_t i; - - for (i = 0; i < setsz; i++) { - if (set[i] == x) - return 1; - } - return 0; -} - -void close_all_fds(const int exclude[], size_t exsz) +void ul_close_all_fds(unsigned int first, unsigned int last) { struct dirent *d; DIR *dir; @@ -149,25 +138,29 @@ void close_all_fds(const int exclude[], size_t exsz) if (dir) { while ((d = xreaddir(dir))) { char *end; - int fd; + unsigned int fd; + int dfd; errno = 0; - fd = strtol(d->d_name, &end, 10); + fd = strtoul(d->d_name, &end, 10); if (errno || end == d->d_name || !end || *end) continue; - if (dirfd(dir) == fd) + dfd = dirfd(dir); + if (dfd < 0) + continue; + if ((unsigned int)dfd == fd) continue; - if (in_set(fd, exclude, exsz)) + if (fd < first || last < fd) continue; close(fd); } closedir(dir); } else { - int fd, tbsz = get_fd_tabsize(); + unsigned fd, tbsz = get_fd_tabsize(); for (fd = 0; fd < tbsz; fd++) { - if (!in_set(fd, exclude, exsz)) + if (first <= fd && fd <= last) close(fd); } } @@ -181,22 +174,23 @@ int main(int argc, char *argv[]) if (strcmp(argv[1], "--mkstemp") == 0) { FILE *f; - char *tmpname; + char *tmpname = NULL; + f = xfmkstemp(&tmpname, NULL, "test"); unlink(tmpname); free(tmpname); fclose(f); } else if (strcmp(argv[1], "--close-fds") == 0) { - static const int wanted_fds[] = { - STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO - }; - ignore_result( dup(STDIN_FILENO) ); ignore_result( dup(STDIN_FILENO) ); ignore_result( dup(STDIN_FILENO) ); - close_all_fds(wanted_fds, ARRAY_SIZE(wanted_fds)); +# ifdef HAVE_CLOSE_RANGE + if (close_range(STDERR_FILENO + 1, ~0U, 0) < 0) +# endif + ul_close_all_fds(STDERR_FILENO + 1, ~0U); + } else if (strcmp(argv[1], "--copy-file") == 0) { int ret = ul_copy_file(STDIN_FILENO, STDOUT_FILENO); if (ret == UL_COPY_READ_ERROR) @@ -209,7 +203,7 @@ int main(int argc, char *argv[]) #endif -int mkdir_p(const char *path, mode_t mode) +int ul_mkdir_p(const char *path, mode_t mode) { char *p, *dir; int rc = 0; diff --git a/src/utils/lib/jsonwrt.c b/src/utils/lib/jsonwrt.c index 00e8b9d..f2003e8 100644 --- a/src/utils/lib/jsonwrt.c +++ b/src/utils/lib/jsonwrt.c @@ -8,16 +8,105 @@ */ #include <stdio.h> #include <inttypes.h> +#include <ctype.h> +#include <cctype.h> #include "c.h" -#include "carefulputc.h" #include "jsonwrt.h" +/* + * Requirements enumerated via testing (V8, Firefox, IE11): + * + * var charsToEscape = []; + * for (var i = 0; i < 65535; i += 1) { + * try { + * JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}'); + * } catch (e) { + * charsToEscape.push(i); + * } + * } + */ +static void fputs_quoted_case_json(const char *data, FILE *out, int dir) +{ + const char *p; + + fputc('"', out); + for (p = data; p && *p; p++) { + + const unsigned int c = (unsigned int) *p; + + /* From http://www.json.org + * + * The double-quote and backslashes would break out a string or + * init an escape sequence if not escaped. + * + * Note that single-quotes and forward slashes, while they're + * in the JSON spec, don't break double-quoted strings. + */ + if (c == '"' || c == '\\') { + fputc('\\', out); + fputc(c, out); + continue; + } + + /* All non-control characters OK; do the case swap as required. */ + if (c >= 0x20) { + /* + * Don't use locale sensitive ctype.h functions for regular + * ASCII chars, because for example with Turkish locale + * (aka LANG=tr_TR.UTF-8) toupper('I') returns 'I'. + */ + if (c <= 127) + fputc(dir == 1 ? c_toupper(c) : + dir == -1 ? c_tolower(c) : *p, out); + else + fputc(dir == 1 ? toupper(c) : + dir == -1 ? tolower(c) : *p, out); + continue; + } + + /* In addition, all chars under ' ' break Node's/V8/Chrome's, and + * Firefox's JSON.parse function + */ + switch (c) { + /* Handle short-hand cases to reduce output size. C + * has most of the same stuff here, so if there's an + * "Escape for C" function somewhere in the STL, we + * should probably be using it. + */ + case '\b': + fputs("\\b", out); + break; + case '\t': + fputs("\\t", out); + break; + case '\n': + fputs("\\n", out); + break; + case '\f': + fputs("\\f", out); + break; + case '\r': + fputs("\\r", out); + break; + default: + /* Other assorted control characters */ + fprintf(out, "\\u00%02x", c); + break; + } + } + fputc('"', out); +} + +#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) void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent) { fmt->out = out; fmt->indent = indent; + fmt->after_close = 0; } void ul_jsonwrt_indent(struct ul_jsonwrt *fmt) @@ -30,12 +119,16 @@ void ul_jsonwrt_indent(struct ul_jsonwrt *fmt) void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type) { - if (fmt->postponed_break && !name) - ; - else { + if (name) { + if (fmt->after_close) + fputs(",\n", fmt->out); ul_jsonwrt_indent(fmt); - if (name) - fputs_quoted_json_lower(name, fmt->out); + fputs_quoted_json_lower(name, fmt->out); + } else { + if (fmt->after_close) + fputs(",", fmt->out); + else + ul_jsonwrt_indent(fmt); } switch (type) { @@ -44,21 +137,22 @@ void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type) fmt->indent++; break; case UL_JSON_ARRAY: - fputs(name ? ": [\n" : "{\n", fmt->out); + fputs(name ? ": [\n" : "[\n", fmt->out); fmt->indent++; break; case UL_JSON_VALUE: fputs(name ? ": " : " ", fmt->out); break; } - fmt->postponed_break = 0; + fmt->after_close = 0; } -void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type, int islast) +void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type) { - if (fmt->indent == 0) { - fputs("}\n", fmt->out); + if (fmt->indent == 1) { + fputs("\n}\n", fmt->out); fmt->indent--; + fmt->after_close = 1; return; } assert(fmt->indent > 0); @@ -66,63 +160,57 @@ void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type, int islast) switch (type) { case UL_JSON_OBJECT: fmt->indent--; + fputc('\n', fmt->out); ul_jsonwrt_indent(fmt); - fputs(islast ? "}" : "},", fmt->out); + fputs("}", fmt->out); break; case UL_JSON_ARRAY: fmt->indent--; + fputc('\n', fmt->out); ul_jsonwrt_indent(fmt); - fputs(islast ? "]" : "],", fmt->out); + fputs("]", 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; - } + fmt->after_close = 1; } void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt, - const char *name, const char *data, int islast) + const char *name, const char *data) { ul_jsonwrt_value_open(fmt, name); if (data && *data) fputs(data, fmt->out); else fputs("null", fmt->out); - ul_jsonwrt_value_close(fmt, islast); + ul_jsonwrt_value_close(fmt); } void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt, - const char *name, const char *data, int islast) + const char *name, const char *data) { 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); + ul_jsonwrt_value_close(fmt); } void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt, - const char *name, uint64_t data, int islast) + const char *name, uint64_t data) { ul_jsonwrt_value_open(fmt, name); fprintf(fmt->out, "%"PRIu64, data); - ul_jsonwrt_value_close(fmt, islast); + ul_jsonwrt_value_close(fmt); } void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt, - const char *name, int data, int islast) + const char *name, int data) { ul_jsonwrt_value_open(fmt, name); fputs(data ? "true" : "false", fmt->out); - ul_jsonwrt_value_close(fmt, islast); + ul_jsonwrt_value_close(fmt); } - diff --git a/src/utils/lib/loopdev.c b/src/utils/lib/loopdev.c index 194daf9..2fb46e2 100644 --- a/src/utils/lib/loopdev.c +++ b/src/utils/lib/loopdev.c @@ -41,6 +41,7 @@ #include "canonicalize.h" #include "blkdev.h" #include "debug.h" +#include "fileutils.h" /* * Debug stuff (based on include/debug.h) @@ -542,7 +543,7 @@ static int loopcxt_next_from_sysfs(struct loopdev_cxt *lc) if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0 - || strncmp(d->d_name, "xloop", 4) != 0) + || strncmp(d->d_name, "xloop", 5) != 0) continue; snprintf(name, sizeof(name), "%s/xloop/backing_file", d->d_name); @@ -634,14 +635,30 @@ done: int is_loopdev(const char *device) { struct stat st; + int rc = 0; - if (device && stat(device, &st) == 0 && - S_ISBLK(st.st_mode) && - major(st.st_rdev) == LOOPDEV_MAJOR) - return 1; + if (!device || stat(device, &st) != 0 || !S_ISBLK(st.st_mode)) + rc = 0; + else if (major(st.st_rdev) == LOOPDEV_MAJOR) + rc = 1; + else if (sysfs_devno_is_wholedisk(st.st_rdev)) { + /* It's possible that kernel creates a device with a different + * major number ... check by /sys it's really xloop device. + */ + char name[PATH_MAX], *cn, *p = NULL; + + snprintf(name, sizeof(name), _PATH_SYS_DEVBLOCK "/%d:%d", + major(st.st_rdev), minor(st.st_rdev)); + cn = canonicalize_path(name); + if (cn) + p = stripoff_last_component(cn); + rc = p && startswith(p, "xloop"); + free(cn); + } - errno = ENODEV; - return 0; + if (rc == 0) + errno = ENODEV; + return rc; } /* @@ -668,12 +685,12 @@ struct loop_info64 *loopcxt_get_info(struct loopdev_cxt *lc) if (ioctl(fd, LOOP_GET_STATUS64, &lc->config.info) == 0) { lc->has_info = 1; lc->info_failed = 0; - DBG(CXT, ul_debugobj(lc, "reading loop_info64 OK")); + DBG(CXT, ul_debugobj(lc, "reading xloop_info64 OK")); return &lc->config.info; } lc->info_failed = 1; - DBG(CXT, ul_debugobj(lc, "reading loop_info64 FAILED")); + DBG(CXT, ul_debugobj(lc, "reading xloop_info64 FAILED")); return NULL; } @@ -1302,7 +1319,7 @@ static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd) } if (blkdev_get_size(dev_fd, (unsigned long long *) &size)) { - DBG(CXT, ul_debugobj(lc, "failed to determine loopdev size")); + DBG(CXT, ul_debugobj(lc, "failed to determine xloopdev size")); return -errno; } @@ -1313,7 +1330,7 @@ static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd) } if (expected_size != size) { - DBG(CXT, ul_debugobj(lc, "warning: loopdev and expected " + DBG(CXT, ul_debugobj(lc, "warning: xloopdev and expected " "size mismatch (%ju/%ju)", size, expected_size)); @@ -1329,7 +1346,7 @@ static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd) if (expected_size != size) { errno = ERANGE; - DBG(CXT, ul_debugobj(lc, "failed to set loopdev size, " + DBG(CXT, ul_debugobj(lc, "failed to set xloopdev size, " "size: %ju, expected: %ju", size, expected_size)); return -errno; @@ -1429,8 +1446,8 @@ int loopcxt_setup_device(struct loopdev_cxt *lc) if (ioctl(dev_fd, LOOP_CONFIGURE, &lc->config) < 0) { rc = -errno; errsv = errno; - if (errno != EINVAL) { - DBG(SETUP, ul_debugobj(lc, "LOOP_CONFIGURE failed: %m")); + if (errno != EINVAL && errno != ENOTTY) { + DBG(SETUP, ul_debugobj(lc, "XLOOP_CONFIGURE failed: %m")); goto err; } fallback = 1; @@ -1440,7 +1457,7 @@ int loopcxt_setup_device(struct loopdev_cxt *lc) errsv = -rc; goto err; } - DBG(SETUP, ul_debugobj(lc, "LOOP_CONFIGURE: OK")); + DBG(SETUP, ul_debugobj(lc, "XLOOP_CONFIGURE: OK")); } /* @@ -1451,11 +1468,11 @@ int loopcxt_setup_device(struct loopdev_cxt *lc) if (ioctl(dev_fd, LOOP_SET_FD, file_fd) < 0) { rc = -errno; errsv = errno; - DBG(SETUP, ul_debugobj(lc, "LOOP_SET_FD failed: %m")); + DBG(SETUP, ul_debugobj(lc, "XLOOP_SET_FD failed: %m")); goto err; } - DBG(SETUP, ul_debugobj(lc, "LOOP_SET_FD: OK")); + DBG(SETUP, ul_debugobj(lc, "XLOOP_SET_FD: OK")); if (lc->blocksize > 0 && (rc = loopcxt_ioctl_blocksize(lc, lc->blocksize)) < 0) { @@ -1473,11 +1490,11 @@ int loopcxt_setup_device(struct loopdev_cxt *lc) if (err) { rc = -errno; errsv = errno; - DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64 failed: %m")); + DBG(SETUP, ul_debugobj(lc, "XLOOP_SET_STATUS64 failed: %m")); goto err; } - DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64: OK")); + DBG(SETUP, ul_debugobj(lc, "XLOOP_SET_STATUS64: OK")); } if ((rc = loopcxt_check_size(lc, file_fd))) @@ -1535,11 +1552,11 @@ int loopcxt_ioctl_status(struct loopdev_cxt *lc) } while (again); if (err) { rc = -errno; - DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64 failed: %m")); + DBG(SETUP, ul_debugobj(lc, "XLOOP_SET_STATUS64 failed: %m")); return rc; } - DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64: OK")); + DBG(SETUP, ul_debugobj(lc, "XLOOP_SET_STATUS64: OK")); return 0; } @@ -1553,7 +1570,7 @@ int loopcxt_ioctl_capacity(struct loopdev_cxt *lc) /* Kernels prior to v2.6.30 don't support this ioctl */ if (ioctl(fd, LOOP_SET_CAPACITY, 0) < 0) { int rc = -errno; - DBG(CXT, ul_debugobj(lc, "LOOP_SET_CAPACITY failed: %m")); + DBG(CXT, ul_debugobj(lc, "XLOOP_SET_CAPACITY failed: %m")); return rc; } @@ -1571,7 +1588,7 @@ int loopcxt_ioctl_dio(struct loopdev_cxt *lc, unsigned long use_dio) /* Kernels prior to v4.4 don't support this ioctl */ if (ioctl(fd, LOOP_SET_DIRECT_IO, use_dio) < 0) { int rc = -errno; - DBG(CXT, ul_debugobj(lc, "LOOP_SET_DIRECT_IO failed: %m")); + DBG(CXT, ul_debugobj(lc, "XLOOP_SET_DIRECT_IO failed: %m")); return rc; } @@ -1593,7 +1610,7 @@ int loopcxt_ioctl_blocksize(struct loopdev_cxt *lc, uint64_t blocksize) /* Kernels prior to v4.14 don't support this ioctl */ if (ioctl(fd, LOOP_SET_BLOCK_SIZE, (unsigned long) blocksize) < 0) { int rc = -errno; - DBG(CXT, ul_debugobj(lc, "LOOP_SET_BLOCK_SIZE failed: %m")); + DBG(CXT, ul_debugobj(lc, "XLOOP_SET_BLOCK_SIZE failed: %m")); return rc; } @@ -1609,7 +1626,7 @@ int loopcxt_delete_device(struct loopdev_cxt *lc) return -EINVAL; if (ioctl(fd, LOOP_CLR_FD, 0) < 0) { - DBG(CXT, ul_debugobj(lc, "LOOP_CLR_FD failed: %m")); + DBG(CXT, ul_debugobj(lc, "XLOOP_CLR_FD failed: %m")); return -errno; } @@ -1862,7 +1879,7 @@ int loopcxt_find_overlap(struct loopdev_cxt *lc, const char *filename, /* full match */ if (lc_sizelimit == sizelimit && lc_offset == offset) { - DBG(CXT, ul_debugobj(lc, "overlapping loop device %s (full match)", + DBG(CXT, ul_debugobj(lc, "overlapping xloop device %s (full match)", loopcxt_get_device(lc))); rc = 2; goto found; @@ -1874,7 +1891,7 @@ int loopcxt_find_overlap(struct loopdev_cxt *lc, const char *filename, if (sizelimit != 0 && offset + sizelimit <= lc_offset) continue; - DBG(CXT, ul_debugobj(lc, "overlapping loop device %s", + DBG(CXT, ul_debugobj(lc, "overlapping xloop device %s", loopcxt_get_device(lc))); rc = 1; goto found; @@ -1949,3 +1966,23 @@ int loopdev_count_by_backing_file(const char *filename, char **loopdev) } return count; } + +#ifdef TEST_PROGRAM_LOOPDEV +int main(int argc, char *argv[]) +{ + if (argc < 2) + goto usage; + + if (strcmp(argv[1], "--is-loopdev") == 0 && argc == 3) + printf("%s: %s\n", argv[2], is_loopdev(argv[2]) ? "OK" : "FAIL"); + else + goto usage; + + return EXIT_SUCCESS; +usage: + fprintf(stderr, "usage: %1$s --is-loopdev <dev>\n", + program_invocation_short_name); + return EXIT_FAILURE; +} +#endif + diff --git a/src/utils/lib/path.c b/src/utils/lib/path.c index 75fa853..21f9bd1 100644 --- a/src/utils/lib/path.c +++ b/src/utils/lib/path.c @@ -542,22 +542,27 @@ DIR *ul_path_opendirf(struct path_cxt *pc, const char *path, ...) ssize_t ul_path_readlink(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path) { int dirfd; + ssize_t ssz; if (!path) { const char *p = get_absdir(pc); if (!p) return -errno; - return readlink(p, buf, bufsiz); - } + ssz = readlink(p, buf, bufsiz - 1); + } else { + dirfd = ul_path_get_dirfd(pc); + if (dirfd < 0) + return dirfd; - dirfd = ul_path_get_dirfd(pc); - if (dirfd < 0) - return dirfd; + if (*path == '/') + path++; - if (*path == '/') - path++; + ssz = readlinkat(dirfd, path, buf, bufsiz - 1); + } - return readlinkat(dirfd, path, buf, bufsiz); + if (ssz >= 0) + buf[ssz] = '\0'; + return ssz; } /* @@ -617,7 +622,7 @@ int ul_path_readf(struct path_cxt *pc, char *buf, size_t len, const char *path, * Returns newly allocated buffer with data from file. Maximal size is BUFSIZ * (send patch if you need something bigger;-) * - * Returns size of the string! + * Returns size of the string without \0, nothing is allocated if returns <= 0. */ int ul_path_read_string(struct path_cxt *pc, char **str, const char *path) { @@ -635,6 +640,8 @@ int ul_path_read_string(struct path_cxt *pc, char **str, const char *path) /* Remove tailing newline (usual in sysfs) */ if (rc > 0 && *(buf + rc - 1) == '\n') --rc; + if (rc == 0) + return 0; buf[rc] = '\0'; *str = strdup(buf); @@ -1099,7 +1106,7 @@ int main(int argc, char *argv[]) ul_path_init_debug(); - pc = ul_new_path(dir); + pc = ul_new_path("%s", dir); if (!pc) err(EXIT_FAILURE, "failed to initialize path context"); if (prefix) @@ -1191,11 +1198,11 @@ int main(int argc, char *argv[]) errx(EXIT_FAILURE, "<file> not defined"); file = argv[optind++]; - if (ul_path_read_string(pc, &res, file) < 0) + if (ul_path_read_string(pc, &res, file) <= 0) err(EXIT_FAILURE, "read string failed"); printf("read: %s: %s\n", file, res); - if (ul_path_readf_string(pc, &res, "%s", file) < 0) + if (ul_path_readf_string(pc, &res, "%s", file) <= 0) err(EXIT_FAILURE, "readf string failed"); printf("readf: %s: %s\n", file, res); diff --git a/src/utils/lib/pty-session.c b/src/utils/lib/pty-session.c index f4bb004..6f038e1 100644 --- a/src/utils/lib/pty-session.c +++ b/src/utils/lib/pty-session.c @@ -18,6 +18,7 @@ #include <paths.h> #include <sys/types.h> #include <sys/wait.h> +#include <inttypes.h> #include "c.h" #include "all-io.h" @@ -129,7 +130,8 @@ void ul_pty_set_mainloop_time(struct ul_pty *pty, struct timeval *tv) } else { pty->next_callback_time.tv_sec = tv->tv_sec; pty->next_callback_time.tv_usec = tv->tv_usec; - DBG(IO, ul_debugobj(pty, "mainloop time: %ld.%06ld", tv->tv_sec, tv->tv_usec)); + DBG(IO, ul_debugobj(pty, "mainloop time: %"PRId64".%06"PRId64, + (int64_t) tv->tv_sec, (int64_t) tv->tv_usec)); } } @@ -239,6 +241,15 @@ void ul_pty_cleanup(struct ul_pty *pty) tcsetattr(STDIN_FILENO, TCSADRAIN, &rtt); } +int ul_pty_chownmod_slave(struct ul_pty *pty, uid_t uid, gid_t gid, mode_t mode) +{ + if (fchown(pty->slave, uid, gid)) + return -errno; + if (fchmod(pty->slave, mode)) + return -errno; + return 0; +} + /* call me in child process */ void ul_pty_init_slave(struct ul_pty *pty) { diff --git a/src/utils/lib/pwdutils.c b/src/utils/lib/pwdutils.c index d97020c..1c1f13e 100644 --- a/src/utils/lib/pwdutils.c +++ b/src/utils/lib/pwdutils.c @@ -3,6 +3,7 @@ * it what you wish. */ #include <stdlib.h> +#include <assert.h> #include "c.h" #include "pwdutils.h" @@ -17,8 +18,8 @@ struct passwd *xgetpwnam(const char *username, char **pwdbuf) struct passwd *pwd = NULL, *res = NULL; int rc; - if (!pwdbuf || !username) - return NULL; + assert(pwdbuf); + assert(username); *pwdbuf = xmalloc(UL_GETPW_BUFSIZ); pwd = xcalloc(1, sizeof(struct passwd)); @@ -49,8 +50,8 @@ struct group *xgetgrnam(const char *groupname, char **grpbuf) struct group *grp = NULL, *res = NULL; int rc; - if (!grpbuf || !groupname) - return NULL; + assert(grpbuf); + assert(groupname); *grpbuf = xmalloc(UL_GETPW_BUFSIZ); grp = xcalloc(1, sizeof(struct group)); @@ -77,8 +78,7 @@ struct passwd *xgetpwuid(uid_t uid, char **pwdbuf) struct passwd *pwd = NULL, *res = NULL; int rc; - if (!pwdbuf) - return NULL; + assert(pwdbuf); *pwdbuf = xmalloc(UL_GETPW_BUFSIZ); pwd = xcalloc(1, sizeof(struct passwd)); @@ -104,11 +104,6 @@ char *xgetlogin(void) { struct passwd *pw = NULL; uid_t ruid; - char *user; - - user = getlogin(); - if (user) - return xstrdup(user); /* GNU Hurd implementation has an extension where a process can exist in a * non-conforming environment, and thus be outside the realms of POSIX @@ -117,6 +112,9 @@ char *xgetlogin(void) * environment. * * http://austingroupbugs.net/view.php?id=511 + * + * The same implementation is useful for other systems, since getlogin(3) + * shouldn't be used as actual identification. */ errno = 0; ruid = getuid(); diff --git a/src/utils/lib/strutils.c b/src/utils/lib/strutils.c index eec4cd5..096aaf5 100644 --- a/src/utils/lib/strutils.c +++ b/src/utils/lib/strutils.c @@ -319,103 +319,125 @@ char *strndup(const char *s, size_t n) } #endif -static uint32_t _strtou32_or_err(const char *str, const char *errmesg, int base); -static uint64_t _strtou64_or_err(const char *str, const char *errmesg, int base); - -int16_t strtos16_or_err(const char *str, const char *errmesg) +/* + * convert strings to numbers; returns <0 on error, and 0 on success + */ +int ul_strtos64(const char *str, int64_t *num, int base) { - int32_t num = strtos32_or_err(str, errmesg); + char *end = NULL; - if (num < INT16_MIN || num > INT16_MAX) { - errno = ERANGE; - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - } - return num; + errno = 0; + if (str == NULL || *str == '\0') + return -EINVAL; + *num = (int64_t) strtoimax(str, &end, base); + + if (errno || str == end || (end && *end)) + return -EINVAL; + return 0; } -static uint16_t _strtou16_or_err(const char *str, const char *errmesg, int base) +int ul_strtou64(const char *str, uint64_t *num, int base) { - uint32_t num = _strtou32_or_err(str, errmesg, base); + char *end = NULL; + int64_t tmp; - if (num > UINT16_MAX) { + errno = 0; + if (str == NULL || *str == '\0') + return -EINVAL; + + /* we need to ignore negative numbers, note that for invalid negative + * number strtoimax() returns negative number too, so we do not + * need to check errno here */ + tmp = (int64_t) strtoimax(str, &end, base); + if (tmp < 0) errno = ERANGE; - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); + else { + errno = 0; + *num = strtoumax(str, &end, base); } - return num; -} -uint16_t strtou16_or_err(const char *str, const char *errmesg) -{ - return _strtou16_or_err(str, errmesg, 10); + if (errno || str == end || (end && *end)) + return -EINVAL; + return 0; } -uint16_t strtox16_or_err(const char *str, const char *errmesg) +int ul_strtos32(const char *str, int32_t *num, int base) { - return _strtou16_or_err(str, errmesg, 16); + int64_t tmp; + int rc; + + rc = ul_strtos64(str, &tmp, base); + if (rc == 0 && (tmp < INT32_MIN || tmp > INT32_MAX)) + rc = -(errno = ERANGE); + if (rc == 0) + *num = (int32_t) tmp; + return rc; } -int32_t strtos32_or_err(const char *str, const char *errmesg) +int ul_strtou32(const char *str, uint32_t *num, int base) { - int64_t num = strtos64_or_err(str, errmesg); - - if (num < INT32_MIN || num > INT32_MAX) { - errno = ERANGE; - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - } - return num; + uint64_t tmp; + int rc; + + rc = ul_strtou64(str, &tmp, base); + if (rc == 0 && tmp > UINT32_MAX) + rc = -(errno = ERANGE); + if (rc == 0) + *num = (uint32_t) tmp; + return rc; } -static uint32_t _strtou32_or_err(const char *str, const char *errmesg, int base) +/* + * Covert strings to numbers in defined range and print message on error. + * + * These functions are used when we read input from users (getopt() etc.). It's + * better to consolidate the code and keep it all based on 64-bit numbers then + * implement it for 32 and 16-bit numbers too. + */ +int64_t str2num_or_err(const char *str, int base, const char *errmesg, + int64_t low, int64_t up) { - uint64_t num = _strtou64_or_err(str, errmesg, base); + int64_t num = 0; + int rc; - if (num > UINT32_MAX) { - errno = ERANGE; - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); + rc = ul_strtos64(str, &num, base); + if (rc == 0 && ((low && num < low) || (up && num > up))) + rc = -(errno = ERANGE); + + if (rc) { + if (errno == ERANGE) + err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); + errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); } return num; } -uint32_t strtou32_or_err(const char *str, const char *errmesg) -{ - return _strtou32_or_err(str, errmesg, 10); -} - -uint32_t strtox32_or_err(const char *str, const char *errmesg) -{ - return _strtou32_or_err(str, errmesg, 16); -} - -int64_t strtos64_or_err(const char *str, const char *errmesg) +uint64_t str2unum_or_err(const char *str, int base, const char *errmesg, uint64_t up) { - int64_t num; - char *end = NULL; - - errno = 0; - if (str == NULL || *str == '\0') - goto err; - num = strtoimax(str, &end, 10); + uint64_t num = 0; + int rc; - if (errno || str == end || (end && *end)) - goto err; + rc = ul_strtou64(str, &num, base); + if (rc == 0 && (up && num > up)) + rc = -(errno = ERANGE); + if (rc) { + if (errno == ERANGE) + err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); + errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); + } return num; -err: - if (errno == ERANGE) - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - - errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); } -static uint64_t _strtou64_or_err(const char *str, const char *errmesg, int base) +double strtod_or_err(const char *str, const char *errmesg) { - uintmax_t num; + double num; char *end = NULL; errno = 0; if (str == NULL || *str == '\0') goto err; - num = strtoumax(str, &end, base); + num = strtod(str, &end); if (errno || str == end || (end && *end)) goto err; @@ -428,17 +450,7 @@ err: errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); } -uint64_t strtou64_or_err(const char *str, const char *errmesg) -{ - return _strtou64_or_err(str, errmesg, 10); -} - -uint64_t strtox64_or_err(const char *str, const char *errmesg) -{ - return _strtou64_or_err(str, errmesg, 16); -} - -double strtod_or_err(const char *str, const char *errmesg) +long double strtold_or_err(const char *str, const char *errmesg) { double num; char *end = NULL; @@ -446,7 +458,7 @@ double strtod_or_err(const char *str, const char *errmesg) errno = 0; if (str == NULL || *str == '\0') goto err; - num = strtod(str, &end); + num = strtold(str, &end); if (errno || str == end || (end && *end)) goto err; @@ -517,11 +529,19 @@ uintmax_t strtosize_or_err(const char *str, const char *errmesg) void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg) { - double user_input; + long double user_input; - user_input = strtod_or_err(str, errmesg); + user_input = strtold_or_err(str, errmesg); tv->tv_sec = (time_t) user_input; - tv->tv_usec = (long)((user_input - tv->tv_sec) * 1000000); + tv->tv_usec = (suseconds_t)((user_input - tv->tv_sec) * 1000000); +} + +time_t strtotime_or_err(const char *str, const char *errmesg) +{ + int64_t user_input; + + user_input = strtos64_or_err(str, errmesg); + return (time_t) user_input; } /* @@ -1045,6 +1065,36 @@ int skip_fline(FILE *fp) } while (1); } + +/* compare two strings, but ignoring non-alnum and case of the characters, for example + * "Hello (123)!" is the same as "hello123". + */ +int ul_stralnumcmp(const char *p1, const char *p2) +{ + const unsigned char *s1 = (const unsigned char *) p1; + const unsigned char *s2 = (const unsigned char *) p2; + unsigned char c1, c2; + + do { + do { + c1 = (unsigned char) *s1++; + } while (c1 != '\0' && !isalnum((unsigned int) c1)); + + do { + c2 = (unsigned char) *s2++; + } while (c2 != '\0' && !isalnum((unsigned int) c2)); + + if (c1 != '\0') + c1 = tolower(c1); + if (c2 != '\0') + c2 = tolower(c2); + if (c1 == '\0') + return c1 - c2; + } while (c1 == c2); + + return c1 - c2; +} + #ifdef TEST_PROGRAM_STRUTILS struct testS { char *name; @@ -1133,23 +1183,49 @@ static int test_strutils_normalize(int argc, char *argv[]) int main(int argc, char *argv[]) { - if (argc == 3 && strcmp(argv[1], "--size") == 0) + if (argc == 3 && strcmp(argv[1], "--size") == 0) { return test_strutils_sizes(argc - 1, argv + 1); - if (argc == 4 && strcmp(argv[1], "--cmp-paths") == 0) + } else if (argc == 4 && strcmp(argv[1], "--cmp-paths") == 0) { return test_strutils_cmp_paths(argc - 1, argv + 1); - if (argc == 4 && strcmp(argv[1], "--strdup-member") == 0) + } else if (argc == 4 && strcmp(argv[1], "--strdup-member") == 0) { return test_strdup_to_member(argc - 1, argv + 1); - else if (argc == 3 && strcmp(argv[1], "--normalize") == 0) + } else if (argc == 4 && strcmp(argv[1], "--stralnumcmp") == 0) { + printf("%s\n", ul_stralnumcmp(argv[2], argv[3]) == 0 ? + "match" : "dismatch"); + return EXIT_SUCCESS; + } else if (argc == 3 && strcmp(argv[1], "--normalize") == 0) { return test_strutils_normalize(argc - 1, argv + 1); - else { + + } else if (argc == 3 && strcmp(argv[1], "--strtos64") == 0) { + printf("'%s'-->%jd\n", argv[2], strtos64_or_err(argv[2], "strtos64 failed")); + return EXIT_SUCCESS; + } else if (argc == 3 && strcmp(argv[1], "--strtou64") == 0) { + printf("'%s'-->%ju\n", argv[2], strtou64_or_err(argv[2], "strtou64 failed")); + return EXIT_SUCCESS; + } else if (argc == 3 && strcmp(argv[1], "--strtos32") == 0) { + printf("'%s'-->%d\n", argv[2], strtos32_or_err(argv[2], "strtos32 failed")); + return EXIT_SUCCESS; + } else if (argc == 3 && strcmp(argv[1], "--strtou32") == 0) { + printf("'%s'-->%u\n", argv[2], strtou32_or_err(argv[2], "strtou32 failed")); + return EXIT_SUCCESS; + } else if (argc == 3 && strcmp(argv[1], "--strtos16") == 0) { + printf("'%s'-->%hd\n", argv[2], strtos16_or_err(argv[2], "strtos16 failed")); + return EXIT_SUCCESS; + } else if (argc == 3 && strcmp(argv[1], "--strtou16") == 0) { + printf("'%s'-->%hu\n", argv[2], strtou16_or_err(argv[2], "strtou16 failed")); + return EXIT_SUCCESS; + + } 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", + " %1$s --stralnumcmp <str> <str>\n" + " %1$s --normalize <str>\n" + " %1$s --strto{s,u}{16,32,64} <str>\n", argv[0]); exit(EXIT_FAILURE); } diff --git a/src/utils/lib/sysfs.c b/src/utils/lib/sysfs.c index 3b75a23..bb71833 100644 --- a/src/utils/lib/sysfs.c +++ b/src/utils/lib/sysfs.c @@ -182,10 +182,9 @@ char *sysfs_blkdev_get_name(struct path_cxt *pc, char *buf, size_t bufsiz) ssize_t sz; /* read /sys/dev/block/<maj:min> link */ - sz = ul_path_readlink(pc, link, sizeof(link) - 1, NULL); + sz = ul_path_readlink(pc, link, sizeof(link), NULL); if (sz < 0) return NULL; - link[sz] = '\0'; name = strrchr(link, '/'); if (!name) @@ -393,7 +392,7 @@ char *sysfs_blkdev_get_devchain(struct path_cxt *pc, char *buf, size_t bufsz) if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > bufsz) return NULL; - buf[sz++] = '\0'; + sz++; prefix = ul_path_get_prefix(pc); if (prefix) psz = strlen(prefix); @@ -567,10 +566,9 @@ int sysfs_blkdev_get_wholedisk( struct path_cxt *pc, char *name; ssize_t linklen; - linklen = ul_path_readlink(pc, linkpath, sizeof(linkpath) - 1, NULL); + linklen = ul_path_readlink(pc, linkpath, sizeof(linkpath), NULL); if (linklen < 0) goto err; - linkpath[linklen] = '\0'; stripoff_last_component(linkpath); /* dirname */ name = stripoff_last_component(linkpath); /* basename */ @@ -678,11 +676,10 @@ int sysfs_blkdev_scsi_get_hctl(struct path_cxt *pc, int *h, int *c, int *t, int goto done; blk->hctl_error = 1; - len = ul_path_readlink(pc, buf, sizeof(buf) - 1, "device"); + len = ul_path_readlink(pc, buf, sizeof(buf), "device"); if (len < 0) return len; - buf[len] = '\0'; hctl = strrchr(buf, '/'); if (!hctl) return -1; diff --git a/src/utils/lib/timeutils.c b/src/utils/lib/timeutils.c index 8b443cd..2e28ada 100644 --- a/src/utils/lib/timeutils.c +++ b/src/utils/lib/timeutils.c @@ -24,6 +24,7 @@ #include <string.h> #include <time.h> #include <sys/time.h> +#include <inttypes.h> #include "c.h" #include "nls.h" @@ -438,14 +439,14 @@ static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf } if (flags & ISO_DOTUSEC) { - len = snprintf(p, bufsz, ".%06ld", (long) usec); + len = snprintf(p, bufsz, ".%06"PRId64, (int64_t) usec); if (len < 0 || (size_t) len > bufsz) goto err; bufsz -= len; p += len; } else if (flags & ISO_COMMAUSEC) { - len = snprintf(p, bufsz, ",%06ld", (long) usec); + len = snprintf(p, bufsz, ",%06"PRId64, (int64_t) usec); if (len < 0 || (size_t) len > bufsz) goto err; bufsz -= len; @@ -480,7 +481,7 @@ int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz) if (rc) return format_iso_time(&tm, tv->tv_usec, flags, buf, bufsz); - warnx(_("time %ld is out of range."), tv->tv_sec); + warnx(_("time %"PRId64" is out of range."), (int64_t)(tv->tv_sec)); return -1; } @@ -504,7 +505,7 @@ int strtime_iso(const time_t *t, int flags, char *buf, size_t bufsz) if (rc) return format_iso_time(&tm, 0, flags, buf, bufsz); - warnx(_("time %ld is out of range."), (long)t); + warnx(_("time %"PRId64" is out of range."), (int64_t)*t); return -1; } @@ -581,7 +582,7 @@ int main(int argc, char *argv[]) } if (strcmp(argv[1], "--timestamp") == 0) { - usec_t usec; + usec_t usec = 0; parse_timestamp(argv[2], &usec); tv.tv_sec = (time_t) (usec / 1000000); diff --git a/src/utils/libsmartcols/src/buffer.c b/src/utils/libsmartcols/src/buffer.c index d376e8f..8a31379 100644 --- a/src/utils/libsmartcols/src/buffer.c +++ b/src/utils/libsmartcols/src/buffer.c @@ -56,6 +56,8 @@ int buffer_append_data(struct libscols_buffer *buf, const char *str) return -EINVAL; if (!str || !*str) return 0; + if (!buf->cur || !buf->begin) + return -EINVAL; sz = strlen(str); maxsz = buf->bufsz - (buf->cur - buf->begin); diff --git a/src/utils/libsmartcols/src/calculate.c b/src/utils/libsmartcols/src/calculate.c index b6137fd..974fc34 100644 --- a/src/utils/libsmartcols/src/calculate.c +++ b/src/utils/libsmartcols/src/calculate.c @@ -325,7 +325,7 @@ int __scols_calculate(struct libscols_table *tb, struct libscols_buffer *buf) DBG(TAB, ul_debugobj(tb, " enlarge width (last column)")); - if (!scols_column_is_right(col) && tb->termwidth - width > 0) { + if (!scols_column_is_right(col)) { col->width += tb->termwidth - width; width = tb->termwidth; } diff --git a/src/utils/libsmartcols/src/libsmartcols.h b/src/utils/libsmartcols/src/libsmartcols.h index 35c24c9..26c00bc 100644 --- a/src/utils/libsmartcols/src/libsmartcols.h +++ b/src/utils/libsmartcols/src/libsmartcols.h @@ -95,7 +95,9 @@ enum { enum { SCOLS_JSON_STRING = 0, /* default */ SCOLS_JSON_NUMBER = 1, - SCOLS_JSON_BOOLEAN = 2 + SCOLS_JSON_BOOLEAN = 2, + SCOLS_JSON_ARRAY_STRING = 3, /* e.g. for multi-line (SCOLS_FL_WRAP) cells */ + SCOLS_JSON_ARRAY_NUMBER = 4 }; /* diff --git a/src/utils/libsmartcols/src/print-api.c b/src/utils/libsmartcols/src/print-api.c index 89dd3bf..237d9ae 100644 --- a/src/utils/libsmartcols/src/print-api.c +++ b/src/utils/libsmartcols/src/print-api.c @@ -119,8 +119,8 @@ static int do_print_table(struct libscols_table *tb, int *is_empty) if (scols_table_is_json(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_array_open(&tb->json, tb->name ? tb->name : ""); + ul_jsonwrt_array_close(&tb->json); ul_jsonwrt_root_close(&tb->json); } else if (is_empty) *is_empty = 1; @@ -134,7 +134,7 @@ static int do_print_table(struct libscols_table *tb, int *is_empty) if (scols_table_is_json(tb)) { ul_jsonwrt_root_open(&tb->json); - ul_jsonwrt_array_open(&tb->json, tb->name); + ul_jsonwrt_array_open(&tb->json, tb->name ? tb->name : ""); } if (tb->format == SCOLS_FMT_HUMAN) @@ -150,7 +150,7 @@ static int do_print_table(struct libscols_table *tb, int *is_empty) rc = __scols_print_table(tb, buf); if (scols_table_is_json(tb)) { - ul_jsonwrt_array_close(&tb->json, 1); + ul_jsonwrt_array_close(&tb->json); ul_jsonwrt_root_close(&tb->json); } done: diff --git a/src/utils/libsmartcols/src/print.c b/src/utils/libsmartcols/src/print.c index 9d78c90..9f60148 100644 --- a/src/utils/libsmartcols/src/print.c +++ b/src/utils/libsmartcols/src/print.c @@ -1,5 +1,4 @@ -/* - * table.c - functions handling the data at the table level + /* print.c - functions to print table * * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com> * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com> @@ -217,16 +216,94 @@ static int has_pending_data(struct libscols_table *tb) return 0; } +static void fputs_color_reset(struct libscols_table *tb) +{ + if (tb->cur_color) { + fputs(UL_COLOR_RESET, tb->out); + tb->cur_color = NULL; + } +} + +static void fputs_color(struct libscols_table *tb, const char *color) +{ + if (tb->cur_color) + fputs_color_reset(tb); + + tb->cur_color = color; + if (color) + fputs(color, tb->out); +} + +static const char *get_cell_color(struct libscols_table *tb, + struct libscols_column *cl, + struct libscols_line *ln, + struct libscols_cell *ce) +{ + const char *color = NULL; + + if (!tb || !tb->colors_wanted || tb->format != SCOLS_FMT_HUMAN) + return NULL; + if (ce) + color = ce->color; + if (!color && (!ln || !ln->color) && cl) + color = cl->color; + return color; +} + +/* switch from line color to cell/column color */ +static void fputs_color_cell_open(struct libscols_table *tb, + struct libscols_column *cl, + struct libscols_line *ln, + struct libscols_cell *ce) +{ + const char *color = get_cell_color(tb, cl, ln, ce); + + if (color) + fputs_color(tb, color); +} + +/* switch from cell/column color to line color or reset */ +static void fputs_color_cell_close(struct libscols_table *tb, + struct libscols_column *cl, + struct libscols_line *ln, + struct libscols_cell *ce) +{ + const char *color = get_cell_color(tb, cl, ln, ce); + + if (color) + fputs_color(tb, ln ? ln->color : NULL); +} + +/* switch to line color */ +static void fputs_color_line_open(struct libscols_table *tb, + struct libscols_line *ln) +{ + if (!tb || !tb->colors_wanted || tb->format != SCOLS_FMT_HUMAN) + return; + fputs_color(tb, ln ? ln->color : NULL); +} + +/* switch off all colors */ +static void fputs_color_line_close(struct libscols_table *tb) +{ + if (!tb || !tb->colors_wanted || tb->format != SCOLS_FMT_HUMAN) + return; + fputs_color_reset(tb); +} + /* print padding or ASCII-art instead of data of @cl */ static void print_empty_cell(struct libscols_table *tb, struct libscols_column *cl, struct libscols_line *ln, /* optional */ + struct libscols_cell *ce, size_t bufsz) { size_t len_pad = 0; /* in screen cells as opposed to bytes */ DBG(COL, ul_debugobj(cl, " printing empty cell")); + fputs_color_cell_open(tb, cl, ln, ce); + /* generate tree ASCII-art rather than padding */ if (ln && scols_column_is_tree(cl)) { if (!ln->parent) { @@ -256,39 +333,28 @@ static void print_empty_cell(struct libscols_table *tb, } /* minout -- don't fill */ - if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) + if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) { + fputs_color_cell_close(tb, cl, ln, ce); return; + } /* default -- fill except last column */ - if (!scols_table_is_maxout(tb) && is_last_column(cl)) + if (!scols_table_is_maxout(tb) && is_last_column(cl)) { + fputs_color_cell_close(tb, cl, ln, ce); return; + } /* fill rest of cell with space */ for(; len_pad < cl->width; ++len_pad) fputs(cellpadding_symbol(tb), tb->out); + fputs_color_cell_close(tb, cl, ln, ce); + if (!is_last_column(cl)) fputs(colsep(tb), tb->out); } -static const char *get_cell_color(struct libscols_table *tb, - struct libscols_column *cl, - struct libscols_line *ln, /* optional */ - struct libscols_cell *ce) /* optional */ -{ - const char *color = NULL; - - if (tb && tb->colors_wanted) { - if (ce) - color = ce->color; - if (ln && !color) - color = ln->color; - if (!color) - color = cl->color; - } - return color; -} /* Fill the start of a line with padding (or with tree ascii-art). * @@ -304,6 +370,7 @@ static const char *get_cell_color(struct libscols_table *tb, static void print_newline_padding(struct libscols_table *tb, struct libscols_column *cl, struct libscols_line *ln, /* optional */ + struct libscols_cell *ce, size_t bufsz) { size_t i; @@ -316,9 +383,13 @@ static void print_newline_padding(struct libscols_table *tb, fputs(linesep(tb), tb->out); /* line break */ tb->termlines_used++; + fputs_color_line_open(tb, ln); + /* fill cells after line break */ for (i = 0; i <= (size_t) cl->seqnum; i++) - print_empty_cell(tb, scols_table_get_column(tb, i), ln, bufsz); + print_empty_cell(tb, scols_table_get_column(tb, i), ln, ce, bufsz); + + fputs_color_line_close(tb); } /* @@ -374,7 +445,6 @@ static int print_pending_data( struct libscols_line *ln, /* optional */ struct libscols_cell *ce) { - const char *color = get_cell_color(tb, cl, ln, ce); size_t width = cl->width, bytes; size_t len = width, i; char *data; @@ -407,25 +477,29 @@ static int print_pending_data( if (bytes) step_pending_data(cl, bytes); - if (color) - fputs(color, tb->out); + fputs_color_cell_open(tb, cl, ln, ce); + fputs(data, tb->out); - if (color) - fputs(UL_COLOR_RESET, tb->out); free(data); /* minout -- don't fill */ - if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) + if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) { + fputs_color_cell_close(tb, cl, ln, ce); return 0; + } /* default -- fill except last column */ - if (!scols_table_is_maxout(tb) && is_last_column(cl)) + if (!scols_table_is_maxout(tb) && is_last_column(cl)) { + fputs_color_cell_close(tb, cl, ln, ce); return 0; + } /* fill rest of cell with space */ for(i = len; i < width; i++) fputs(cellpadding_symbol(tb), tb->out); + fputs_color_cell_close(tb, cl, ln, ce); + if (!is_last_column(cl)) fputs(colsep(tb), tb->out); @@ -435,6 +509,49 @@ err: return -errno; } +static void print_json_data(struct libscols_table *tb, + struct libscols_column *cl, + const char *name, + char *data) +{ + switch (cl->json_type) { + case SCOLS_JSON_STRING: + /* name: "aaa" */ + ul_jsonwrt_value_s(&tb->json, name, data); + break; + case SCOLS_JSON_NUMBER: + /* name: 123 */ + ul_jsonwrt_value_raw(&tb->json, name, data); + break; + case SCOLS_JSON_BOOLEAN: + /* name: true|false */ + ul_jsonwrt_value_boolean(&tb->json, name, + !*data ? 0 : + *data == '0' ? 0 : + *data == 'N' || *data == 'n' ? 0 : 1); + break; + case SCOLS_JSON_ARRAY_STRING: + case SCOLS_JSON_ARRAY_NUMBER: + /* name: [ "aaa", "bbb", "ccc" ] */ + ul_jsonwrt_array_open(&tb->json, name); + + if (!scols_column_is_customwrap(cl)) + ul_jsonwrt_value_s(&tb->json, NULL, data); + else do { + char *next = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data); + + if (cl->json_type == SCOLS_JSON_ARRAY_STRING) + ul_jsonwrt_value_s(&tb->json, NULL, data); + else + ul_jsonwrt_value_raw(&tb->json, NULL, data); + data = next; + } while (data); + + ul_jsonwrt_array_close(&tb->json); + break; + } +} + static int print_data(struct libscols_table *tb, struct libscols_column *cl, struct libscols_line *ln, /* optional */ @@ -442,7 +559,6 @@ static int print_data(struct libscols_table *tb, struct libscols_buffer *buf) { size_t len = 0, i, width, bytes; - const char *color = NULL; char *data, *nextchunk; const char *name = NULL; int is_last; @@ -472,36 +588,23 @@ static int print_data(struct libscols_table *tb, return 0; case SCOLS_FMT_EXPORT: - fprintf(tb->out, "%s=", name); + fputs_shell_ident(name, tb->out); + if (endswith(name, "%")) + fputs("PCT", tb->out); + fputc('=', tb->out); fputs_quoted(data, tb->out); if (!is_last) fputs(colsep(tb), tb->out); return 0; case SCOLS_FMT_JSON: - switch (cl->json_type) { - 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; - } + print_json_data(tb, cl, name, data); return 0; case SCOLS_FMT_HUMAN: break; /* continue below */ } - color = get_cell_color(tb, cl, ln, ce); - /* Encode. Note that 'len' and 'width' are number of cells, not bytes. */ data = buffer_get_safe_data(tb, buf, &len, scols_column_get_safechars(cl)); @@ -549,49 +652,39 @@ static int print_data(struct libscols_table *tb, data = NULL; } + fputs_color_cell_open(tb, cl, ln, ce); + if (data && *data) { if (scols_column_is_right(cl)) { - if (color) - fputs(color, tb->out); for (i = len; i < width; i++) fputs(cellpadding_symbol(tb), tb->out); - fputs(data, tb->out); - if (color) - fputs(UL_COLOR_RESET, tb->out); len = width; + } + fputs(data, tb->out); - } else if (color) { - char *p = data; - size_t art = buffer_get_safe_art_size(buf); - - /* we don't want to colorize tree ascii art */ - if (scols_column_is_tree(cl) && art && art < bytes) { - fwrite(p, 1, art, tb->out); - p += art; - } - - fputs(color, tb->out); - fputs(p, tb->out); - fputs(UL_COLOR_RESET, tb->out); - } else - fputs(data, tb->out); } /* minout -- don't fill */ - if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) + if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) { + fputs_color_cell_close(tb, cl, ln, ce); return 0; + } /* default -- fill except last column */ - if (!scols_table_is_maxout(tb) && is_last) + if (!scols_table_is_maxout(tb) && is_last) { + fputs_color_cell_close(tb, cl, ln, ce); return 0; + } /* fill rest of cell with space */ for(i = len; i < width; i++) fputs(cellpadding_symbol(tb), tb->out); + fputs_color_cell_close(tb, cl, ln, ce); + if (len > width && !scols_column_is_trunc(cl)) { DBG(COL, ul_debugobj(cl, "*** data len=%zu > column width=%zu", len, width)); - print_newline_padding(tb, cl, ln, buffer_get_size(buf)); /* next column starts on next line */ + print_newline_padding(tb, cl, ln, ce, buffer_get_size(buf)); /* next column starts on next line */ } else if (!is_last) fputs(colsep(tb), tb->out); /* columns separator */ @@ -664,6 +757,8 @@ static int print_line(struct libscols_table *tb, DBG(LINE, ul_debugobj(ln, "printing line")); + fputs_color_line_open(tb, ln); + /* regular line */ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { @@ -677,12 +772,14 @@ static int print_line(struct libscols_table *tb, if (rc == 0 && cl->pending_data) pending = 1; } + fputs_color_line_close(tb); /* extra lines of the multi-line cells */ while (rc == 0 && pending) { DBG(LINE, ul_debugobj(ln, "printing pending data")); pending = 0; fputs(linesep(tb), tb->out); + fputs_color_line_open(tb, ln); tb->termlines_used++; scols_reset_iter(&itr, SCOLS_ITER_FORWARD); while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { @@ -693,8 +790,9 @@ static int print_line(struct libscols_table *tb, if (rc == 0 && cl->pending_data) pending = 1; } else - print_empty_cell(tb, cl, ln, buffer_get_size(buf)); + print_empty_cell(tb, cl, ln, NULL, buffer_get_size(buf)); } + fputs_color_line_close(tb); } return 0; @@ -702,7 +800,7 @@ static int print_line(struct libscols_table *tb, int __scols_print_title(struct libscols_table *tb) { - int rc, color = 0; + int rc; mbs_align_t align; size_t width, len = 0, bufsz, titlesz; char *title = NULL, *buf = NULL; @@ -783,15 +881,14 @@ int __scols_print_title(struct libscols_table *tb) goto done; } - if (tb->colors_wanted && tb->title.color) - color = 1; - if (color) - fputs(tb->title.color, tb->out); + + if (tb->colors_wanted) + fputs_color(tb, tb->title.color); fputs(title, tb->out); - if (color) - fputs(UL_COLOR_RESET, tb->out); + if (tb->colors_wanted) + fputs_color_reset(tb); fputc('\n', tb->out); rc = 0; @@ -877,7 +974,7 @@ int __scols_print_range(struct libscols_table *tb, rc = print_line(tb, ln, buf); if (scols_table_is_json(tb)) - ul_jsonwrt_object_close(&tb->json, last); + ul_jsonwrt_object_close(&tb->json); else if (last == 0 && tb->no_linesep == 0) { fputs(linesep(tb), tb->out); tb->termlines_used++; @@ -937,9 +1034,9 @@ 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)); - ul_jsonwrt_object_close(&tb->json, last); + ul_jsonwrt_object_close(&tb->json); if (last && is_child(ln)) - ul_jsonwrt_array_close(&tb->json, last); + ul_jsonwrt_array_close(&tb->json); ln = ln->parent; } while(ln && last); diff --git a/src/utils/libsmartcols/src/smartcolsP.h b/src/utils/libsmartcols/src/smartcolsP.h index 5e9b9ec..810cc0f 100644 --- a/src/utils/libsmartcols/src/smartcolsP.h +++ b/src/utils/libsmartcols/src/smartcolsP.h @@ -124,7 +124,7 @@ struct libscols_column { struct libscols_cell header; - struct list_head cl_columns; + struct list_head cl_columns; /* member of table->tb_columns */ struct libscols_table *table; @@ -212,8 +212,8 @@ struct libscols_table { char *colsep; /* column separator */ char *linesep; /* line separator */ - struct list_head tb_columns; - struct list_head tb_lines; + struct list_head tb_columns; /* list of columns, items: column->cl_columns */ + struct list_head tb_lines; /* list of lines; items: line->ln_lines */ struct list_head tb_groups; /* all defined groups */ struct libscols_group **grpset; @@ -222,6 +222,8 @@ struct libscols_table { size_t ngrpchlds_pending; /* groups with not yet printed children */ struct libscols_line *walk_last_tree_root; /* last root, used by scols_walk_() */ + struct libscols_column *dflt_sort_column; /* default sort column, set by scols_sort_table() */ + struct libscols_symbols *symbols; struct libscols_cell title; /* optional table title (for humans) */ @@ -232,6 +234,8 @@ struct libscols_table { size_t termlines_used; /* printed line counter */ size_t header_next; /* where repeat header */ + const char *cur_color; /* current active color when printing */ + /* flags */ unsigned int ascii :1, /* don't use unicode */ colors_wanted :1, /* enable colors */ diff --git a/src/utils/libsmartcols/src/table.c b/src/utils/libsmartcols/src/table.c index a3ba21d..cbc5fd4 100644 --- a/src/utils/libsmartcols/src/table.c +++ b/src/utils/libsmartcols/src/table.c @@ -50,7 +50,7 @@ static void check_padding_debug(struct libscols_table *tb) { const char *str; - assert(libsmartcols_debug_mask); /* debug has to be enabled! */ + assert(libsmartcols_debug_mask); /* debug has to be already initialized! */ str = getenv("LIBSMARTCOLS_DEBUG_PADDING"); if (!str || (strcmp(str, "on") != 0 && strcmp(str, "1") != 0)) @@ -270,6 +270,8 @@ int scols_table_remove_column(struct libscols_table *tb, if (cl->flags & SCOLS_FL_TREE) tb->ntreecols--; + if (tb->dflt_sort_column == cl) + tb->dflt_sort_column = NULL; DBG(TAB, ul_debugobj(tb, "remove column")); list_del_init(&cl->cl_columns); @@ -1076,6 +1078,10 @@ int scols_table_enable_json(struct libscols_table *tb, int enable) * Enable/disable export output format (COLUMNAME="value" ...). * The parsable output formats (export and raw) are mutually exclusive. * + * Note that COLUMNAME maybe be modified on output to contains only chars + * allowed as shell variable identifiers, for example MIN-IO and FSUSE% will be + * MIN_IO and FSUSE_PCT. + * * Returns: 0 on success, negative number in case of an error. */ int scols_table_enable_export(struct libscols_table *tb, int enable) @@ -1511,41 +1517,63 @@ static int sort_line_children(struct libscols_line *ln, struct libscols_column * return 0; } +static int __scols_sort_tree(struct libscols_table *tb, struct libscols_column *cl) +{ + struct libscols_line *ln; + struct libscols_iter itr; + + if (!tb || !cl || !cl->cmpfunc) + return -EINVAL; + + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_table_next_line(tb, &itr, &ln) == 0) + sort_line_children(ln, cl); + return 0; +} + /** * scols_sort_table: * @tb: table - * @cl: order by this column + * @cl: order by this column or NULL * * Orders the table by the column. See also scols_column_set_cmpfunc(). If the * tree output is enabled then children in the tree are recursively sorted too. * + * The column @cl is saved as the default sort column to the @tb and the next time + * is possible to call scols_sort_table(tb, NULL). The saved column is also used by + * scols_sort_table_by_tree(). + * * Returns: 0, a negative value in case of an error. */ int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl) { - if (!tb || !cl || !cl->cmpfunc) + if (!tb) + return -EINVAL; + if (!cl) + cl = tb->dflt_sort_column; + if (!cl || !cl->cmpfunc) return -EINVAL; - DBG(TAB, ul_debugobj(tb, "sorting table")); + DBG(TAB, ul_debugobj(tb, "sorting table by %zu column", cl->seqnum)); list_sort(&tb->tb_lines, cells_cmp_wrapper_lines, cl); - if (scols_table_is_tree(tb)) { - struct libscols_line *ln; - struct libscols_iter itr; + if (scols_table_is_tree(tb)) + __scols_sort_tree(tb, cl); - scols_reset_iter(&itr, SCOLS_ITER_FORWARD); - while (scols_table_next_line(tb, &itr, &ln) == 0) - sort_line_children(ln, cl); - } + if (cl && cl != tb->dflt_sort_column) + tb->dflt_sort_column = cl; return 0; } +/* + * Move all @ln's children after @ln in the table. + */ static struct libscols_line *move_line_and_children(struct libscols_line *ln, struct libscols_line *pre) { if (pre) { list_del_init(&ln->ln_lines); /* remove from old position */ - list_add(&ln->ln_lines, &pre->ln_lines); /* add to the new place (behind @pre) */ + list_add(&ln->ln_lines, &pre->ln_lines); /* add to the new place (after @pre) */ } pre = ln; @@ -1567,7 +1595,10 @@ static struct libscols_line *move_line_and_children(struct libscols_line *ln, st * @tb: table * * Reorders lines in the table by parent->child relation. Note that order of - * the lines in the table is independent on the tree hierarchy. + * the lines in the table is independent on the tree hierarchy by default. + * + * The children of the lines are sorted according to the default sort column + * if scols_sort_table() has been previously called. * * Since: 2.30 * @@ -1583,13 +1614,12 @@ int scols_sort_table_by_tree(struct libscols_table *tb) DBG(TAB, ul_debugobj(tb, "sorting table by tree")); - scols_reset_iter(&itr, SCOLS_ITER_FORWARD); - while (scols_table_next_line(tb, &itr, &ln) == 0) { - if (ln->parent) - continue; + if (tb->dflt_sort_column) + __scols_sort_tree(tb, tb->dflt_sort_column); + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_table_next_line(tb, &itr, &ln) == 0) move_line_and_children(ln, NULL); - } return 0; } diff --git a/src/utils/sys-utils/bugreports.adoc b/src/utils/sys-utils/bugreports.adoc new file mode 100644 index 0000000..3949ea0 --- /dev/null +++ b/src/utils/sys-utils/bugreports.adoc @@ -0,0 +1,3 @@ +== REPORTING BUGS + +For bug reports, use the issue tracker at https://github.com/bwLehrpool/xloop/issues. diff --git a/src/utils/sys-utils/footer.adoc b/src/utils/sys-utils/footer.adoc new file mode 100644 index 0000000..cc33241 --- /dev/null +++ b/src/utils/sys-utils/footer.adoc @@ -0,0 +1,4 @@ +== AVAILABILITY + +// TRANSLATORS: Keep *{command}* untranslated, it will be replaced with the command name. +The *{command}* command is part of the xloop package which can be downloaded from https://github.com/bwLehrpool/xloop[bwLehrpool]. diff --git a/src/utils/sys-utils/xlosetup.8 b/src/utils/sys-utils/xlosetup.8 index c948899..2b596ac 100644 --- a/src/utils/sys-utils/xlosetup.8 +++ b/src/utils/sys-utils/xlosetup.8 @@ -1,222 +1,246 @@ -.TH XLOSETUP 8 "September 2020" "util-linux" "System Administration" -.SH NAME -xlosetup \- set up and control xloop devices -.SH SYNOPSIS +'\" t +.\" Title: xlosetup +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.16 +.\" Date: 2021-12-07 +.\" Manual: System Administration +.\" Source: xloop-util +.\" Language: English +.\" +.TH "XLOSETUP" "8" "2021-12-07" "xloop\-util" "System Administration" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh .ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +xlosetup \- set up and control xloop devices +.SH "SYNOPSIS" +.sp Get info: .sp -.in +5 -.B xlosetup -[\fIxloopdev\fP] +\fBxlosetup\fP [\fIxloopdev\fP] .sp -.B xlosetup \-l -.RB [ \-a ] +\fBxlosetup\fP \fB\-l\fP [\fB\-a\fP] .sp -.B xlosetup \-j -.I file -.RB [ \-o -.IR offset ] +\fBxlosetup\fP \fB\-j\fP \fIfile\fP [\fB\-o\fP \fIoffset\fP] .sp -.in -5 Detach a xloop device: .sp -.in +5 -.B "xlosetup \-d" -.IR xloopdev ... +\fBxlosetup\fP \fB\-d\fP \fIxloopdev\fP ... .sp -.in -5 Detach all associated xloop devices: .sp -.in +5 -.B "xlosetup \-D" +\fBxlosetup\fP \fB\-D\fP .sp -.in -5 Set up a xloop device: .sp -.in +5 -.B xlosetup -.RB [ \-o -.IR offset ] -.RB [ \-\-sizelimit -.IR size ] -.RB [ \-\-sector\-size -.IR size ] -.in +8 -.RB [ \-Pr ] -.RB [ \-\-show ] " \-f" | \fIxloopdev\fP -.I file -.sp -.in -13 +\fBxlosetup\fP [\fB\-o\fP \fIoffset\fP] [\fB\-\-sizelimit\fP \fIsize\fP] [\fB\-\-sector\-size\fP \fIsize\fP] [\fB\-Pr\fP] [\fB\-\-show\fP] \fB\-f\fP \fIxloopdev file\fP +.sp Resize a xloop device: .sp -.in +5 -.B "xlosetup \-c" -.I xloopdev -.in -5 -.ad b -.SH DESCRIPTION -.B xlosetup -is used to associate xloop devices with regular files or block devices, -to detach xloop devices, and to query the status of a xloop device. If only the -\fIxloopdev\fP argument is given, the status of the corresponding xloop -device is shown. If no option is given, all xloop devices are shown. -.sp -Note that the old output format (i.e., \fBxlosetup \-a\fR) with comma-delimited -strings is deprecated in favour of the \fB\-\-list\fR output format. -.sp -It's possible to create more independent xloop devices for the same backing -file. -.B This setup may be dangerous, can cause data loss, corruption and overwrites. -Use \fB\-\-nooverlap\fR with \fB\-\-find\fR during setup to avoid this problem. -.sp -The xloop device setup is not an atomic operation when used with \fB\-\-find\fP, and -.B xlosetup -does not protect this operation by any lock. The number of attempts is -internally restricted to a maximum of 64. It is recommended to use for example -.BR flock (1) -to avoid a collision in heavily parallel use cases. - -.SH OPTIONS -The \fIsize\fR and \fIoffset\fR -arguments may be followed by the multiplicative suffixes KiB (=1024), -MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB" is -optional, e.g., "K" has the same meaning as "KiB") or the suffixes -KB (=1000), MB (=1000*1000), and so on for GB, TB, PB, EB, ZB and YB. - -.TP -.BR \-a , " \-\-all" -Show the status of all xloop devices. Note that not all information is accessible -for non-root users. See also \fB\-\-list\fR. The old output format (as printed -without \fB\-\-list)\fR is deprecated. -.TP -.BR \-d , " \-\-detach " \fIxloopdev\fR... -Detach the file or device associated with the specified xloop device(s). Note -that since Linux v3.7 kernel uses "lazy device destruction". The detach -operation does not return EBUSY error anymore if device is actively used by -system, but it is marked by autoclear flag and destroyed later. -.TP -.BR \-D , " \-\-detach\-all" +\fBxlosetup\fP \fB\-c\fP \fIxloopdev\fP +.SH "DESCRIPTION" +.sp +\fBxlosetup\fP is used to associate xloop devices with regular files or block devices, to detach xloop devices, and to query the status of a xloop device. If only the \fIxloopdev\fP argument is given, the status of the corresponding xloop device is shown. If no option is given, all xloop devices are shown. +.sp +Note that the old output format (i.e., \fBxlosetup \-a\fP) with comma\-delimited strings is deprecated in favour of the \fB\-\-list\fP output format. +.sp +It\(cqs possible to create more independent xloop devices for the same backing file. \fBThis setup may be dangerous, can cause data loss, corruption and overwrites.\fP Use \fB\-\-nooverlap\fP with \fB\-\-find\fP during setup to avoid this problem. +.sp +The xloop device setup is not an atomic operation when used with \fB\-\-find\fP, and \fBxlosetup\fP does not protect this operation by any lock. The number of attempts is internally restricted to a maximum of 16. It is recommended to use for example flock1 to avoid a collision in heavily parallel use cases. +.SH "OPTIONS" +.sp +The \fIsize\fP and \fIoffset\fP arguments may be followed by the multiplicative suffixes KiB (=1024), MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB" is optional, e.g., "K" has the same meaning as "KiB") or the suffixes KB (=1000), MB (=1000*1000), and so on for GB, TB, PB, EB, ZB and YB. +.sp +\fB\-a\fP, \fB\-\-all\fP +.RS 4 +Show the status of all xloop devices. Note that not all information is accessible for non\-root users. See also \fB\-\-list\fP. The old output format (as printed without \fB\-\-list)\fP is deprecated. +.RE +.sp +\fB\-d\fP, \fB\-\-detach\fP \fIxloopdev\fP... +.RS 4 +Detach the file or device associated with the specified xloop device(s). Note that since Linux v3.7 kernel uses "lazy device destruction". The detach operation does not return \fBEBUSY\fP error anymore if device is actively used by system, but it is marked by autoclear flag and destroyed later. +.RE +.sp +\fB\-D\fP, \fB\-\-detach\-all\fP +.RS 4 Detach all associated xloop devices. -.TP -.BR \-f , " \-\-find " "\fR[\fIfile\fR]" -Find the first unused xloop device. If a \fIfile\fR argument is present, use -the found device as xloop device. Otherwise, just print its name. -.IP "\fB\-\-show\fP" -Display the name of the assigned xloop device if the \fB\-f\fP option and a -\fIfile\fP argument are present. -.TP -.BR \-L , " \-\-nooverlap" -Check for conflicts between xloop devices to avoid situation when the same -backing file is shared between more xloop devices. If the file is already used -by another device then re-use the device rather than a new one. The option -makes sense only with \fB\-\-find\fP. -.TP -.BR \-j , " \-\-associated " \fIfile\fR " \fR[\fB\-o \fIoffset\fR]" -Show the status of all xloop devices associated with the given \fIfile\fR. -.TP -.BR \-o , " \-\-offset " \fIoffset -The data start is moved \fIoffset\fP bytes into the specified file or device. The \fIoffset\fP -may be followed by the multiplicative suffixes; see above. -.IP "\fB\-\-sizelimit \fIsize\fP" -The data end is set to no more than \fIsize\fP bytes after the data start. The \fIsize\fP -may be followed by the multiplicative suffixes; see above. -.TP -.BR \-b , " \-\-sector-size " \fIsize -Set the logical sector size of the xloop device in bytes (since Linux 4.14). The -option may be used when create a new xloop device as well as stand-alone command -to modify sector size of the already existing xloop device. -.TP -.BR \-c , " \-\-set\-capacity " \fIxloopdev -Force the xloop driver to reread the size of the file associated with the -specified xloop device. -.TP -.BR \-P , " \-\-partscan" -Force the kernel to scan the partition table on a newly created xloop device. Note that the -partition table parsing depends on sector sizes. The default is sector size is 512 bytes, -otherwise you need to use the option \fB\-\-sector\-size\fR together with \fB\-\-partscan\fR. -.TP -.BR \-r , " \-\-read\-only" -Set up a read-only xloop device. -.TP -.BR \-\-direct\-io [ =on | off ] -Enable or disable direct I/O for the backing file. The optional argument -can be either \fBon\fR or \fBoff\fR. If the argument is omitted, it defaults -to \fBoff\fR. -.TP -.BR \-t , " \-\-type \fIformat\fR" -Set the file format type of the xloop device. If no file format type is specified, -the RAW file format is used by default. Valid file formats are: \fBRAW\fR, -\fBQCOW\fR, \fBVDI\fR, \fBVMDK\fR. -.TP -.BR \-v , " \-\-verbose" +.RE +.sp +\fB\-f\fP, \fB\-\-find\fP [\fIfile\fP] +.RS 4 +Find the first unused xloop device. If a \fIfile\fP argument is present, use the found device as xloop device. Otherwise, just print its name. +.RE +.sp +\fB\-\-show\fP +.RS 4 +Display the name of the assigned xloop device if the \fB\-f\fP option and a \fIfile\fP argument are present. +.RE +.sp +\fB\-L\fP, \fB\-\-nooverlap\fP +.RS 4 +Check for conflicts between xloop devices to avoid situation when the same backing file is shared between more xloop devices. If the file is already used by another device then re\-use the device rather than a new one. The option makes sense only with \fB\-\-find\fP. +.RE +.sp +\fB\-j\fP, \fB\-\-associated\fP \fIfile\fP [\fB\-o\fP \fIoffset\fP] +.RS 4 +Show the status of all xloop devices associated with the given \fIfile\fP. +.RE +.sp +\fB\-o\fP, \fB\-\-offset\fP \fIoffset\fP +.RS 4 +The data start is moved \fIoffset\fP bytes into the specified file or device. The \fIoffset\fP may be followed by the multiplicative suffixes; see above. +.RE +.sp +\fB\-\-sizelimit\fP \fIsize\fP +.RS 4 +The data end is set to no more than \fIsize\fP bytes after the data start. The \fIsize\fP may be followed by the multiplicative suffixes; see above. +.RE +.sp +\fB\-b\fP, \fB\-\-sector\-size\fP \fIsize\fP +.RS 4 +Set the logical sector size of the xloop device in bytes (since Linux 4.14). The option may be used when create a new xloop device as well as stand\-alone command to modify sector size of the already existing xloop device. +.RE +.sp +\fB\-c\fP, \fB\-\-set\-capacity\fP \fIxloopdev\fP +.RS 4 +Force the xloop driver to reread the size of the file associated with the specified xloop device. +.RE +.sp +\fB\-P\fP, \fB\-\-partscan\fP +.RS 4 +Force the kernel to scan the partition table on a newly created xloop device. Note that the partition table parsing depends on sector sizes. The default is sector size is 512 bytes, otherwise you need to use the option \fB\-\-sector\-size\fP together with \fB\-\-partscan\fP. +.RE +.sp +\fB\-r\fP, \fB\-\-read\-only\fP +.RS 4 +Set up a read\-only xloop device. +.RE +.sp +\fB\-\-direct\-io\fP[\fB=on\fP|\fBoff\fP] +.RS 4 +Enable or disable direct I/O for the backing file. The optional argument can be either \fBon\fP or \fBoff\fP. If the argument is omitted, it defaults to \fBoff\fP. +.RE +.sp +\fB\-t\fP, \fB\-\-type\fP \fIformat\fP +.RS 4 +Set the file format type of the xloop device. If no file format type is specified, the RAW file format is used by default. Valid file formats are \fBRAW\fP, \fBQCOW\fP, \fBVDI\fP, and \fBVMDK\fP. +.RE +.sp +\fB\-v\fP, \fB\-\-verbose\fP +.RS 4 Verbose mode. -.TP -.BR \-l , " \-\-list" -If a xloop device or the \fB\-a\fR option is specified, print the default columns -for either the specified xloop device or all xloop devices; the default is to -print info about all devices. See also \fB\-\-output\fP, \fB\-\-noheadings\fP, -\fB\-\-raw\fP, and \fB\-\-json\fP. -.TP -.BR \-O , " \-\-output " \fIcolumn\fR[,\fIcolumn\fR]... -Specify the columns that are to be printed for the \fB\-\-list\fP output. -Use \fB\-\-help\fR to get a list of all supported columns. -.TP -.B \-\-output\-all +.RE +.sp +\fB\-l\fP, \fB\-\-list\fP +.RS 4 +If a xloop device or the \fB\-a\fP option is specified, print the default columns for either the specified xloop device or all xloop devices; the default is to print info about all devices. See also \fB\-\-output\fP, \fB\-\-noheadings\fP, \fB\-\-raw\fP, and \fB\-\-json\fP. +.RE +.sp +\fB\-O\fP, \fB\-\-output\fP \fIcolumn\fP[,\fIcolumn\fP]... +.RS 4 +Specify the columns that are to be printed for the \fB\-\-list\fP output. Use \fB\-\-help\fP to get a list of all supported columns. +.RE +.sp +\fB\-\-output\-all\fP +.RS 4 Output all available columns. -.TP -.BR \-n , " \-\-noheadings" -Don't print headings for \fB\-\-list\fP output format. -.IP "\fB\-\-raw\fP" +.RE +.sp +\fB\-n\fP, \fB\-\-noheadings\fP +.RS 4 +Don\(cqt print headings for \fB\-\-list\fP output format. +.RE +.sp +\fB\-\-raw\fP +.RS 4 Use the raw \fB\-\-list\fP output format. -.TP -.BR \-J , " \-\-json" +.RE +.sp +\fB\-J\fP, \fB\-\-json\fP +.RS 4 Use JSON format for \fB\-\-list\fP output. -.TP -.BR \-V , " \-\-version" +.RE +.sp +\fB\-V\fP, \fB\-\-version\fP +.RS 4 Display version information and exit. -.TP -.BR \-h , " \-\-help" +.RE +.sp +\fB\-h\fP, \fB\-\-help\fP +.RS 4 Display help text and exit. - -.SH ENCRYPTION -.B Cryptoloop is no longer supported in favor of dm-crypt. -.B For more details see cryptsetup(8). - -.SH EXIT STATUS -.B xlosetup -returns 0 on success, nonzero on failure. When -.B xlosetup -displays the status of a xloop device, it returns 1 if the device -is not configured and 2 if an error occurred which prevented -determining the status of the device. - -.SH ENVIRONMENT -.IP XLOOPDEV_DEBUG=all +.RE +.SH "ENCRYPTION" +.sp +\fBCryptoxloop is no longer supported in favor of dm\-crypt.\fP For more details see \fBcryptsetup\fP(8). +.SH "EXIT STATUS" +.sp +\fBxlosetup\fP returns 0 on success, nonzero on failure. When \fBxlosetup\fP displays the status of a xloop device, it returns 1 if the device is not configured and 2 if an error occurred which prevented determining the status of the device. +.SH "NOTES" +.sp +Since version 2.37 \fBxlosetup\fP uses \fBXLOOP_CONFIGURE\fP ioctl to setup a new xloop device by one ioctl call. The old versions use \fBXLOOP_SET_FD\fP and \fBXLOOP_SET_STATUS64\fP ioctls to do the same. +.SH "ENVIRONMENT" +.sp +XLOOPDEV_DEBUG=all +.RS 4 enables debug output. - -.SH FILES -.TP -.I /dev/xloop[0..N] +.RE +.SH "FILES" +.sp +\fI/dev/xloop[0..N]\fP +.RS 4 xloop block devices -.TP -.I /dev/xloop-control +.RE +.sp +\fI/dev/xloop\-control\fP +.RS 4 xloop control device -.SH EXAMPLE +.RE +.SH "EXAMPLE" +.sp The following commands can be used as an example of using the xloop device. +.sp +.if n .RS 4 .nf -.IP +.fam C # dd if=/dev/zero of=~/file.img bs=1024k count=10 # xlosetup \-\-find \-\-show ~/file.img /dev/xloop0 # mkfs \-t ext2 /dev/xloop0 # mount /dev/xloop0 /mnt - ... +\&... # umount /dev/xloop0 # xlosetup \-\-detach /dev/xloop0 +.fam .fi -.SH AUTHORS -Karel Zak <kzak@redhat.com>, based on the original version from -Theodore Ts'o <tytso@athena.mit.edu> -.SH AVAILABILITY -The xlosetup command is part of the util-linux package and is available from -https://www.kernel.org/pub/linux/utils/util-linux/. +.if n .RE +.SH "AUTHORS" +.sp +.MTO "development\(atmanuel\-bentele.de" "Manuel Bentele" "," +.MTO "kzak\(atredhat.com" "Karel Zak" "," +based on the original version from +.MTO "tytso\(atathena.mit.edu" "Theodore Ts\(cqo" "." +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/bwLehrpool/xloop/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBxlosetup\fP command is part of the xloop package which can be downloaded from \c +.URL "https://github.com/bwLehrpool/xloop" "bwLehrpool" "."
\ No newline at end of file diff --git a/src/utils/sys-utils/xlosetup.8.adoc b/src/utils/sys-utils/xlosetup.8.adoc new file mode 100644 index 0000000..59599a3 --- /dev/null +++ b/src/utils/sys-utils/xlosetup.8.adoc @@ -0,0 +1,173 @@ +//po4a: entry man manual += xlosetup(8) +:doctype: manpage +:man manual: System Administration +:man source: xloop-util +:page-layout: base +:command: xlosetup + +== NAME + +xlosetup - set up and control xloop devices + +== SYNOPSIS + +Get info: + +*xlosetup* [_xloopdev_] + +*xlosetup* *-l* [*-a*] + +*xlosetup* *-j* _file_ [*-o* _offset_] + +Detach a xloop device: + +*xlosetup* *-d* _xloopdev_ ... + +Detach all associated xloop devices: + +*xlosetup* *-D* + +Set up a xloop device: + +*xlosetup* [*-o* _offset_] [*--sizelimit* _size_] [*--sector-size* _size_] [*-Pr*] [*--show*] *-f* _xloopdev file_ + +Resize a xloop device: + +*xlosetup* *-c* _xloopdev_ + +== DESCRIPTION + +*xlosetup* is used to associate xloop devices with regular files or block devices, to detach xloop devices, and to query the status of a xloop device. If only the _xloopdev_ argument is given, the status of the corresponding xloop device is shown. If no option is given, all xloop devices are shown. + +Note that the old output format (i.e., *xlosetup -a*) with comma-delimited strings is deprecated in favour of the *--list* output format. + +It's possible to create more independent xloop devices for the same backing file. *This setup may be dangerous, can cause data loss, corruption and overwrites.* Use *--nooverlap* with *--find* during setup to avoid this problem. + +The xloop device setup is not an atomic operation when used with *--find*, and *xlosetup* does not protect this operation by any lock. The number of attempts is internally restricted to a maximum of 16. It is recommended to use for example flock1 to avoid a collision in heavily parallel use cases. + +== OPTIONS + +The _size_ and _offset_ arguments may be followed by the multiplicative suffixes KiB (=1024), MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB" is optional, e.g., "K" has the same meaning as "KiB") or the suffixes KB (=1000), MB (=1000*1000), and so on for GB, TB, PB, EB, ZB and YB. + +*-a*, *--all*:: +Show the status of all xloop devices. Note that not all information is accessible for non-root users. See also *--list*. The old output format (as printed without *--list)* is deprecated. + +*-d*, *--detach* _xloopdev_...:: +Detach the file or device associated with the specified xloop device(s). Note that since Linux v3.7 kernel uses "lazy device destruction". The detach operation does not return *EBUSY* error anymore if device is actively used by system, but it is marked by autoclear flag and destroyed later. + +*-D*, *--detach-all*:: +Detach all associated xloop devices. + +*-f*, *--find* [_file_]:: +Find the first unused xloop device. If a _file_ argument is present, use the found device as xloop device. Otherwise, just print its name. + +*--show*:: +Display the name of the assigned xloop device if the *-f* option and a _file_ argument are present. + +*-L*, *--nooverlap*:: +Check for conflicts between xloop devices to avoid situation when the same backing file is shared between more xloop devices. If the file is already used by another device then re-use the device rather than a new one. The option makes sense only with *--find*. + +*-j*, *--associated* _file_ [*-o* _offset_]:: +Show the status of all xloop devices associated with the given _file_. + +*-o*, *--offset* _offset_:: +The data start is moved _offset_ bytes into the specified file or device. The _offset_ may be followed by the multiplicative suffixes; see above. + +*--sizelimit* _size_:: +The data end is set to no more than _size_ bytes after the data start. The _size_ may be followed by the multiplicative suffixes; see above. + +*-b*, *--sector-size* _size_:: +Set the logical sector size of the xloop device in bytes (since Linux 4.14). The option may be used when create a new xloop device as well as stand-alone command to modify sector size of the already existing xloop device. + +*-c*, *--set-capacity* _xloopdev_:: +Force the xloop driver to reread the size of the file associated with the specified xloop device. + +*-P*, *--partscan*:: +Force the kernel to scan the partition table on a newly created xloop device. Note that the partition table parsing depends on sector sizes. The default is sector size is 512 bytes, otherwise you need to use the option *--sector-size* together with *--partscan*. + +*-r*, *--read-only*:: +Set up a read-only xloop device. + +*--direct-io*[**=on**|*off*]:: +Enable or disable direct I/O for the backing file. The optional argument can be either *on* or *off*. If the argument is omitted, it defaults to *off*. + +*-t*, *--type* _format_:: +Set the file format type of the xloop device. If no file format type is specified, the RAW file format is used by default. Valid file formats are *RAW*, *QCOW*, *VDI*, and *VMDK*. + +*-v*, *--verbose*:: +Verbose mode. + +*-l*, *--list*:: +If a xloop device or the *-a* option is specified, print the default columns for either the specified xloop device or all xloop devices; the default is to print info about all devices. See also *--output*, *--noheadings*, *--raw*, and *--json*. + +*-O*, *--output* _column_[,_column_]...:: +Specify the columns that are to be printed for the *--list* output. Use *--help* to get a list of all supported columns. + +*--output-all*:: +Output all available columns. + +*-n*, *--noheadings*:: +Don't print headings for *--list* output format. + +*--raw*:: +Use the raw *--list* output format. + +*-J*, *--json*:: +Use JSON format for *--list* output. + +*-V*, *--version*:: +Display version information and exit. + +*-h*, *--help*:: +Display help text and exit. + +== ENCRYPTION + +*Cryptoxloop is no longer supported in favor of dm-crypt.* For more details see *cryptsetup*(8). + +== EXIT STATUS + +*xlosetup* returns 0 on success, nonzero on failure. When *xlosetup* displays the status of a xloop device, it returns 1 if the device is not configured and 2 if an error occurred which prevented determining the status of the device. + +== NOTES + +Since version 2.37 *xlosetup* uses *XLOOP_CONFIGURE* ioctl to setup a new xloop device by one ioctl call. The old versions use *XLOOP_SET_FD* and *XLOOP_SET_STATUS64* ioctls to do the same. + +== ENVIRONMENT + +XLOOPDEV_DEBUG=all:: +enables debug output. + +== FILES + +_/dev/xloop[0..N]_:: +xloop block devices + +_/dev/xloop-control_:: +xloop control device + +== EXAMPLE + +The following commands can be used as an example of using the xloop device. + + # dd if=/dev/zero of=~/file.img bs=1024k count=10 + # xlosetup --find --show ~/file.img + /dev/xloop0 + # mkfs -t ext2 /dev/xloop0 + # mount /dev/xloop0 /mnt + ... + # umount /dev/xloop0 + # xlosetup --detach /dev/xloop0 + +== AUTHORS + +mailto:development@manuel-bentele.de[Manuel Bentele], mailto:kzak@redhat.com[Karel Zak], based on the original version from mailto:tytso@athena.mit.edu[Theodore Ts'o]. + +include::bugreports.adoc[] + +include::footer.adoc[] + +ifdef::translation[] +include::translation.adoc[] +endif::[] diff --git a/src/utils/sys-utils/xlosetup.c b/src/utils/sys-utils/xlosetup.c index 768cee9..2511633 100644 --- a/src/utils/sys-utils/xlosetup.c +++ b/src/utils/sys-utils/xlosetup.c @@ -463,6 +463,29 @@ static void __attribute__((__noreturn__)) usage(void) exit(EXIT_SUCCESS); } +static void warn_size(const char *filename, uint64_t size, uint64_t offset, int flags) +{ + struct stat st; + + if (!size) { + if (stat(filename, &st) || S_ISBLK(st.st_mode)) + return; + size = st.st_size; + + if (flags & LOOPDEV_FL_OFFSET) + size -= offset; + } + + if (size < 512) + warnx(_("%s: Warning: file is smaller than 512 bytes; the xloop device " + "may be useless or invisible for system tools."), + filename); + else if (size % 512) + warnx(_("%s: Warning: file does not fit into a 512-byte sector; " + "the end of the file will be ignored."), + filename); +} + static int create_loop(struct loopdev_cxt *lc, int nooverlap, int lo_flags, int flags, const char *file, uint64_t offset, uint64_t sizelimit, @@ -572,7 +595,7 @@ static int create_loop(struct loopdev_cxt *lc, if (rc == 0) break; /* success */ - if (errno == EBUSY && !hasdev && ntries < 64) { + if ((errno == EBUSY || errno == EAGAIN) && !hasdev && ntries < 64) { xusleep(200000); ntries++; continue; @@ -857,6 +880,7 @@ int main(int argc, char **argv) if (res == 0) { if (showdev) printf("%s\n", loopcxt_get_device(&lc)); + warn_size(file, sizelimit, offset, flags); if (set_dio) goto lo_set_dio; } |