diff options
Diffstat (limited to 'src/utils/lib')
-rw-r--r-- | src/utils/lib/canonicalize.c | 3 | ||||
-rw-r--r-- | src/utils/lib/fileutils.c | 46 | ||||
-rw-r--r-- | src/utils/lib/jsonwrt.c | 148 | ||||
-rw-r--r-- | src/utils/lib/loopdev.c | 91 | ||||
-rw-r--r-- | src/utils/lib/path.c | 31 | ||||
-rw-r--r-- | src/utils/lib/pty-session.c | 13 | ||||
-rw-r--r-- | src/utils/lib/pwdutils.c | 20 | ||||
-rw-r--r-- | src/utils/lib/strutils.c | 242 | ||||
-rw-r--r-- | src/utils/lib/sysfs.c | 11 | ||||
-rw-r--r-- | src/utils/lib/timeutils.c | 11 |
10 files changed, 412 insertions, 204 deletions
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); |