ps: fix overflow in USER and VSZ columns
[oweals/busybox.git] / libbb / xfuncs.c
index 7f870ac8b5ce65c9ce320e39991642fd6e8dd62e..6d50bf9bc71ca6bec082ebbfbd63540470dc089a 100644 (file)
@@ -9,7 +9,7 @@
  * Licensed under GPL version 2, see file LICENSE in this tarball for details.
  */
 
-#include "busybox.h"
+#include "libbb.h"
 
 /* All the functions starting with "x" call bb_error_msg_and_die() if they
  * fail, so callers never need to check for errors.  If it returned, it
  * Since dmalloc's prototypes overwrite the impls here as they are
  * included after these prototypes in libbb.h, all is well.
  */
+// Warn if we can't allocate size bytes of memory.
+void *malloc_or_warn(size_t size)
+{
+       void *ptr = malloc(size);
+       if (ptr == NULL && size != 0)
+               bb_error_msg(bb_msg_memory_exhausted);
+       return ptr;
+}
+
 // Die if we can't allocate size bytes of memory.
 void *xmalloc(size_t size)
 {
@@ -97,40 +106,78 @@ FILE *xfopen(const char *path, const char *mode)
 {
        FILE *fp = fopen(path, mode);
        if (fp == NULL)
-               bb_perror_msg_and_die("%s", path);
+               bb_perror_msg_and_die("can't open '%s'", path);
        return fp;
 }
 
-// Die if we can't open an existing file and return an fd.
-int xopen(const char *pathname, int flags)
+// Die if we can't open a file and return a fd.
+int xopen3(const char *pathname, int flags, int mode)
 {
-       //if (ENABLE_DEBUG && (flags & O_CREAT))
-       //      bb_error_msg_and_die("xopen() with O_CREAT");
+       int ret;
+
+       ret = open(pathname, flags, mode);
+       if (ret < 0) {
+               bb_perror_msg_and_die("can't open '%s'", pathname);
+       }
+       return ret;
+}
 
+// Die if we can't open an existing file and return a fd.
+int xopen(const char *pathname, int flags)
+{
        return xopen3(pathname, flags, 0666);
 }
 
-// Die if we can't open a new file and return an fd.
-int xopen3(const char *pathname, int flags, int mode)
+// Warn if we can't open a file and return a fd.
+int open3_or_warn(const char *pathname, int flags, int mode)
 {
        int ret;
 
        ret = open(pathname, flags, mode);
        if (ret < 0) {
-               bb_perror_msg_and_die("%s", pathname);
+               bb_perror_msg("can't open '%s'", pathname);
        }
        return ret;
 }
 
+// Warn if we can't open a file and return a fd.
+int open_or_warn(const char *pathname, int flags)
+{
+       return open3_or_warn(pathname, flags, 0666);
+}
+
+void xpipe(int filedes[2])
+{
+       if (pipe(filedes))
+               bb_perror_msg_and_die("can't create pipe");
+}
+
+void xunlink(const char *pathname)
+{
+       if (unlink(pathname))
+               bb_perror_msg_and_die("can't remove file '%s'", pathname);
+}
+
 // Turn on nonblocking I/O on a fd
 int ndelay_on(int fd)
 {
-       return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
+       return fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) | O_NONBLOCK);
+}
+
+int close_on_exec_on(int fd)
+{
+        return fcntl(fd, F_SETFD, FD_CLOEXEC);
 }
 
 int ndelay_off(int fd)
 {
-       return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
+       return fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) & ~O_NONBLOCK);
+}
+
+void xdup2(int from, int to)
+{
+       if (dup2(from, to) != to)
+               bb_perror_msg_and_die("can't duplicate file descriptor");
 }
 
 // "Renumber" opened fd
@@ -138,8 +185,7 @@ void xmove_fd(int from, int to)
 {
        if (from == to)
                return;
-       if (dup2(from, to) != to)
-               bb_perror_msg_and_die("cannot duplicate file descriptor");
+       xdup2(from, to);
        close(from);
 }
 
@@ -169,6 +215,7 @@ off_t xlseek(int fd, off_t offset, int whence)
 void die_if_ferror(FILE *fp, const char *fn)
 {
        if (ferror(fp)) {
+               /* ferror doesn't set useful errno */
                bb_error_msg_and_die("%s: I/O error", fn);
        }
 }
@@ -187,77 +234,164 @@ void xfflush_stdout(void)
        }
 }
 
-// Wait for the specified child PID to exit, returning child's error return.
-int wait4pid(int pid)
+void sig_block(int sig)
+{
+       sigset_t ss;
+       sigemptyset(&ss);
+       sigaddset(&ss, sig);
+       sigprocmask(SIG_BLOCK, &ss, NULL);
+}
+
+void sig_unblock(int sig)
 {
-       int status;
+       sigset_t ss;
+       sigemptyset(&ss);
+       sigaddset(&ss, sig);
+       sigprocmask(SIG_UNBLOCK, &ss, NULL);
+}
 
-       if (pid <= 0) {
-               errno = ECHILD;
-               return -1;
-       }
-       if (waitpid(pid, &status, 0) == -1)
-               return -1;
-       if (WIFEXITED(status))
-               return WEXITSTATUS(status);
-       if (WIFSIGNALED(status))
-               return WTERMSIG(status) + 10000;
-       return 0;
+#if 0
+void sig_blocknone(void)
+{
+       sigset_t ss;
+       sigemptyset(&ss);
+       sigprocmask(SIG_SETMASK, &ss, NULL);
+}
+#endif
+
+void sig_catch(int sig, void (*f)(int))
+{
+       struct sigaction sa;
+       sa.sa_handler = f;
+       sa.sa_flags = 0;
+       sigemptyset(&sa.sa_mask);
+       sigaction(sig, &sa, NULL);
+}
+
+void sig_pause(void)
+{
+       sigset_t ss;
+       sigemptyset(&ss);
+       sigsuspend(&ss);
 }
 
+
 void xsetenv(const char *key, const char *value)
 {
        if (setenv(key, value, 1))
                bb_error_msg_and_die(bb_msg_memory_exhausted);
 }
 
-// Converts unsigned long long value into compact 4-char
-// representation. Examples: "1234", "1.2k", " 27M", "123T"
-// Fifth char is always '\0'
-void smart_ulltoa5(unsigned long long ul, char buf[5])
+/* Converts unsigned long long value into compact 4-char
+ * representation. Examples: "1234", "1.2k", " 27M", "123T"
+ * String is not terminated (buf[4] is untouched) */
+void smart_ulltoa4(unsigned long long ul, char buf[5], const char *scale)
 {
        const char *fmt;
        char c;
-       unsigned v,idx = 0;
-       ul *= 10;
-       if (ul > 9999*10) { // do not scale if 9999 or less
-               while (ul >= 10000) {
+       unsigned v, u, idx = 0;
+
+       if (ul > 9999) { // do not scale if 9999 or less
+               ul *= 10;
+               do {
                        ul /= 1024;
                        idx++;
-               }
+               } while (ul >= 10000);
        }
        v = ul; // ullong divisions are expensive, avoid them
 
        fmt = " 123456789";
-       if (!idx) {             // 9999 or less: use 1234 format
-               c = buf[0] = " 123456789"[v/10000];
+       u = v / 10;
+       v = v % 10;
+       if (!idx) {
+               // 9999 or less: use "1234" format
+               // u is value/10, v is last digit
+               c = buf[0] = " 123456789"[u/100];
                if (c != ' ') fmt = "0123456789";
-               c = buf[1] = fmt[v/1000%10];
+               c = buf[1] = fmt[u/10%10];
                if (c != ' ') fmt = "0123456789";
-               buf[2] = fmt[v/100%10];
-               buf[3] = "0123456789"[v/10%10];
+               buf[2] = fmt[u%10];
+               buf[3] = "0123456789"[v];
        } else {
-               if (v >= 10*10) {       // scaled value is >=10: use 123M format
-                       c = buf[0] = " 123456789"[v/1000];
+               // u is value, v is 1/10ths (allows for 9.2M format)
+               if (u >= 10) {
+                       // value is >= 10: use "123M', " 12M" formats
+                       c = buf[0] = " 123456789"[u/100];
                        if (c != ' ') fmt = "0123456789";
-                       buf[1] = fmt[v/100%10];
-                       buf[2] = "0123456789"[v/10%10];
-               } else {        // scaled value is <10: use 1.2M format
-                       buf[0] = "0123456789"[v/10];
+                       v = u % 10;
+                       u = u / 10;
+                       buf[1] = fmt[u%10];
+               } else {
+                       // value is < 10: use "9.2M" format
+                       buf[0] = "0123456789"[u];
                        buf[1] = '.';
-                       buf[2] = "0123456789"[v%10];
                }
-               // see http://en.wikipedia.org/wiki/Tera
-               buf[3] = " kMGTPEZY"[idx];
+               buf[2] = "0123456789"[v];
+               buf[3] = scale[idx]; /* typically scale = " kmgt..." */
        }
-       buf[4] = '\0';
 }
 
-// Convert unsigned integer to ascii, writing into supplied buffer.  A
-// truncated result is always null terminated (unless buflen is 0), and
-// contains the first few digits of the result ala strncpy.
+/* Converts unsigned long long value into compact 5-char representation.
+ * String is not terminated (buf[5] is untouched) */
+void smart_ulltoa5(unsigned long long ul, char buf[6], const char *scale)
+{
+       const char *fmt;
+       char c;
+       unsigned v, u, idx = 0;
+
+       if (ul > 99999) { // do not scale if 99999 or less
+               ul *= 10;
+               do {
+                       ul /= 1024;
+                       idx++;
+               } while (ul >= 100000);
+       }
+       v = ul; // ullong divisions are expensive, avoid them
+
+       fmt = " 123456789";
+       u = v / 10;
+       v = v % 10;
+       if (!idx) {
+               // 99999 or less: use "12345" format
+               // u is value/10, v is last digit
+               c = buf[0] = " 123456789"[u/1000];
+               if (c != ' ') fmt = "0123456789";
+               c = buf[1] = fmt[u/100%10];
+               if (c != ' ') fmt = "0123456789";
+               c = buf[2] = fmt[u/10%10];
+               if (c != ' ') fmt = "0123456789";
+               buf[3] = fmt[u%10];
+               buf[4] = "0123456789"[v];
+       } else {
+               // value has been scaled into 0..9999.9 range
+               // u is value, v is 1/10ths (allows for 92.1M format)
+               if (u >= 100) {
+                       // value is >= 100: use "1234M', " 123M" formats
+                       c = buf[0] = " 123456789"[u/1000];
+                       if (c != ' ') fmt = "0123456789";
+                       c = buf[1] = fmt[u/100%10];
+                       if (c != ' ') fmt = "0123456789";
+                       v = u % 10;
+                       u = u / 10;
+                       buf[2] = fmt[u%10];
+               } else {
+                       // value is < 100: use "92.1M" format
+                       c = buf[0] = " 123456789"[u/10];
+                       if (c != ' ') fmt = "0123456789";
+                       buf[1] = fmt[u%10];
+                       buf[2] = '.';
+               }
+               buf[3] = "0123456789"[v];
+               buf[4] = scale[idx]; /* typically scale = " kmgt..." */
+       }
+}
+
+
+// Convert unsigned integer to ascii, writing into supplied buffer.
+// A truncated result contains the first few digits of the result ala strncpy.
+// Returns a pointer past last generated digit, does _not_ store NUL.
 void BUG_sizeof_unsigned_not_4(void);
-void utoa_to_buf(unsigned n, char *buf, unsigned buflen)
+char *utoa_to_buf(unsigned n, char *buf, unsigned buflen)
 {
        unsigned i, out, res;
        if (sizeof(unsigned) != 4)
@@ -273,19 +407,19 @@ void utoa_to_buf(unsigned n, char *buf, unsigned buflen)
                                *buf++ = '0' + res;
                        }
                }
-               *buf = '\0';
        }
+       return buf;
 }
 
 // Convert signed integer to ascii, like utoa_to_buf()
-void itoa_to_buf(int n, char *buf, unsigned buflen)
+char *itoa_to_buf(int n, char *buf, unsigned buflen)
 {
        if (buflen && n<0) {
                n = -n;
                *buf++ = '-';
                buflen--;
        }
-       utoa_to_buf((unsigned)n, buf, buflen);
+       return utoa_to_buf((unsigned)n, buf, buflen);
 }
 
 // The following two functions use a static buffer, so calling either one a
@@ -300,7 +434,7 @@ static char local_buf[12];
 // Convert unsigned integer to ascii using a static buffer (returned).
 char *utoa(unsigned n)
 {
-       utoa_to_buf(n, local_buf, sizeof(local_buf));
+       *(utoa_to_buf(n, local_buf, sizeof(local_buf))) = '\0';
 
        return local_buf;
 }
@@ -308,7 +442,7 @@ char *utoa(unsigned n)
 // Convert signed integer to ascii using a static buffer (returned).
 char *itoa(int n)
 {
-       itoa_to_buf(n, local_buf, sizeof(local_buf));
+       *(itoa_to_buf(n, local_buf, sizeof(local_buf))) = '\0';
 
        return local_buf;
 }
@@ -331,13 +465,13 @@ char *bin2hex(char *p, const char *cp, int count)
 // setgid() will fail and we'll _still_be_root_, which is bad.)
 void xsetgid(gid_t gid)
 {
-       if (setgid(gid)) bb_error_msg_and_die("setgid");
+       if (setgid(gid)) bb_perror_msg_and_die("setgid");
 }
 
 // Die with an error message if we can't set uid.  (See xsetgid() for why.)
 void xsetuid(uid_t uid)
 {
-       if (setuid(uid)) bb_error_msg_and_die("setuid");
+       if (setuid(uid)) bb_perror_msg_and_die("setuid");
 }
 
 // Return how long the file at fd is, if there's any way to determine it.
@@ -380,6 +514,14 @@ off_t fdlength(int fd)
        return pos + 1;
 }
 
+int bb_putchar(int ch)
+{
+       /* time.c needs putc(ch, stdout), not putchar(ch).
+        * it does "stdout = stderr;", but then glibc's putchar()
+        * doesn't work as expected. bad glibc, bad */
+       return putc(ch, stdout);
+}
+
 // Die with an error message if we can't malloc() enough space and do an
 // sprintf() into that space.
 char *xasprintf(const char *format, ...)
@@ -404,7 +546,8 @@ char *xasprintf(const char *format, ...)
        va_end(p);
 #endif
 
-       if (r < 0) bb_error_msg_and_die(bb_msg_memory_exhausted);
+       if (r < 0)
+               bb_error_msg_and_die(bb_msg_memory_exhausted);
        return string_ptr;
 }
 
@@ -423,12 +566,14 @@ int fdprintf(int fd, const char *format, ...)
 #else
        // Bloat for systems that haven't got the GNU extension.
        va_start(p, format);
-       r = vsnprintf(NULL, 0, format, p);
-       va_end(p);
-       string_ptr = xmalloc(r+1);
-       va_start(p, format);
-       r = vsnprintf(string_ptr, r+1, format, p);
+       r = vsnprintf(NULL, 0, format, p) + 1;
        va_end(p);
+       string_ptr = malloc(r);
+       if (string_ptr) {
+               va_start(p, format);
+               r = vsnprintf(string_ptr, r, format, p);
+               va_end(p);
+       }
 #endif
 
        if (r >= 0) {
@@ -446,7 +591,7 @@ void xprint_and_close_file(FILE *file)
        fflush(stdout);
        // copyfd outputs error messages for us.
        if (bb_copyfd_eof(fileno(file), 1) == -1)
-               exit(xfunc_error_retval);
+               xfunc_die();
 
        fclose(file);
 }
@@ -465,7 +610,7 @@ DIR *warn_opendir(const char *path)
 
        dp = opendir(path);
        if (!dp)
-               bb_perror_msg("cannot open '%s'", path);
+               bb_perror_msg("can't open '%s'", path);
        return dp;
 }
 
@@ -476,7 +621,7 @@ DIR *xopendir(const char *path)
 
        dp = opendir(path);
        if (!dp)
-               bb_perror_msg_and_die("cannot open '%s'", path);
+               bb_perror_msg_and_die("can't open '%s'", path);
        return dp;
 }
 
@@ -485,7 +630,18 @@ int xsocket(int domain, int type, int protocol)
 {
        int r = socket(domain, type, protocol);
 
-       if (r < 0) bb_perror_msg_and_die("socket");
+       if (r < 0) {
+               /* Hijack vaguely related config option */
+#if ENABLE_VERBOSE_RESOLUTION_ERRORS
+               const char *s = "INET";
+               if (domain == AF_PACKET) s = "PACKET";
+               if (domain == AF_NETLINK) s = "NETLINK";
+USE_FEATURE_IPV6(if (domain == AF_INET6) s = "INET6";)
+               bb_perror_msg_and_die("socket(AF_%s)", s);
+#else
+               bb_perror_msg_and_die("socket");
+#endif
+       }
 
        return r;
 }
@@ -502,6 +658,20 @@ void xlisten(int s, int backlog)
        if (listen(s, backlog)) bb_perror_msg_and_die("listen");
 }
 
+/* Die with an error message if sendto failed.
+ * Return bytes sent otherwise  */
+ssize_t xsendto(int s, const  void *buf, size_t len, const struct sockaddr *to,
+                               socklen_t tolen)
+{
+       ssize_t ret = sendto(s, buf, len, 0, to, tolen);
+       if (ret < 0) {
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       close(s);
+               bb_perror_msg_and_die("sendto");
+       }
+       return ret;
+}
+
 // xstat() - a stat() which dies on failure with meaningful error message
 void xstat(const char *name, struct stat *stat_buf)
 {
@@ -526,7 +696,7 @@ void selinux_or_die(void)
 
 /* It is perfectly ok to pass in a NULL for either width or for
  * height, in which case that value will not be set.  */
-int get_terminal_width_height(const int fd, int *width, int *height)
+int get_terminal_width_height(int fd, int *width, int *height)
 {
        struct winsize win = { 0, 0, 0, 0 };
        int ret = ioctl(fd, TIOCGWINSZ, &win);
@@ -553,3 +723,61 @@ int get_terminal_width_height(const int fd, int *width, int *height)
 
        return ret;
 }
+
+void ioctl_or_perror_and_die(int fd, int request, void *argp, const char *fmt,...)
+{
+       va_list p;
+
+       if (ioctl(fd, request, argp) < 0) {
+               va_start(p, fmt);
+               bb_verror_msg(fmt, p, strerror(errno));
+               /* xfunc_die can actually longjmp, so be nice */
+               va_end(p);
+               xfunc_die();
+       }
+}
+
+int ioctl_or_perror(int fd, int request, void *argp, const char *fmt,...)
+{
+       va_list p;
+       int ret = ioctl(fd, request, argp);
+
+       if (ret < 0) {
+               va_start(p, fmt);
+               bb_verror_msg(fmt, p, strerror(errno));
+               va_end(p);
+       }
+       return ret;
+}
+
+#if ENABLE_IOCTL_HEX2STR_ERROR
+int bb_ioctl_or_warn(int fd, int request, void *argp, const char *ioctl_name)
+{
+       int ret;
+
+       ret = ioctl(fd, request, argp);
+       if (ret < 0)
+               bb_simple_perror_msg(ioctl_name);
+       return ret;
+}
+void bb_xioctl(int fd, int request, void *argp, const char *ioctl_name)
+{
+       if (ioctl(fd, request, argp) < 0)
+               bb_simple_perror_msg_and_die(ioctl_name);
+}
+#else
+int bb_ioctl_or_warn(int fd, int request, void *argp)
+{
+       int ret;
+
+       ret = ioctl(fd, request, argp);
+       if (ret < 0)
+               bb_perror_msg("ioctl %#x failed", request);
+       return ret;
+}
+void bb_xioctl(int fd, int request, void *argp)
+{
+       if (ioctl(fd, request, argp) < 0)
+               bb_perror_msg_and_die("ioctl %#x failed", request);
+}
+#endif