* 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)
{
if (s == NULL)
return NULL;
- t = strdup (s);
+ t = strdup(s);
if (t == NULL)
bb_error_msg_and_die(bb_msg_memory_exhausted);
// the (possibly truncated to length n) string into it.
char * xstrndup(const char *s, int n)
{
+ int m;
char *t;
if (ENABLE_DEBUG && s == NULL)
bb_error_msg_and_die("xstrndup bug");
- t = xmalloc(++n);
+ /* We can just xmalloc(n+1) and strncpy into it, */
+ /* but think about xstrndup("abc", 10000) wastage! */
+ m = n;
+ t = (char*) s;
+ while (m) {
+ if (!*t) break;
+ m--;
+ t++;
+ }
+ n -= m;
+ t = xmalloc(n + 1);
+ t[n] = '\0';
- return safe_strncpy(t,s,n);
+ return memcpy(t, s, n);
}
// Die if we can't open a file and return a FILE * to it.
// Notice we haven't got xfread(), This is for use with fscanf() and friends.
FILE *xfopen(const char *path, const char *mode)
{
- FILE *fp;
- if ((fp = fopen(path, mode)) == NULL)
- bb_perror_msg_and_die("%s", path);
+ FILE *fp = fopen(path, mode);
+ if (fp == NULL)
+ 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;
- return xopen3(pathname, flags, 0777);
+ 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 a new file and return an fd.
-int xopen3(const char *pathname, int flags, int mode)
+// 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);
+}
+
+// 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;
}
-// Die with an error message if we can't read the entire buffer.
-void xread(int fd, void *buf, size_t count)
+// Warn if we can't open a file and return a fd.
+int open_or_warn(const char *pathname, int flags)
{
- while (count) {
- ssize_t size = safe_read(fd, buf, count);
- if (size < 1)
- bb_error_msg_and_die("short read");
- count -= size;
- buf = ((char *) buf) + size;
- }
+ return open3_or_warn(pathname, flags, 0666);
}
-// Die with an error message if we can't write the entire buffer.
-void xwrite(int fd, void *buf, size_t count)
+void xpipe(int filedes[2])
{
- while (count) {
- ssize_t size = safe_write(fd, buf, count);
- if (size < 1)
- bb_error_msg_and_die("short write");
- count -= size;
- buf = ((char *) buf) + size;
- }
+ if (pipe(filedes))
+ bb_perror_msg_and_die("can't create pipe");
}
-// Die with an error message if we can't lseek to the right spot.
-void xlseek(int fd, off_t offset, int whence)
+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) | O_NONBLOCK);
+}
+
+int close_on_exec_on(int fd)
{
- if (offset != lseek(fd, offset, whence))
- bb_error_msg_and_die("lseek");
+ return fcntl(fd, F_SETFD, FD_CLOEXEC);
}
-// Die with an error message if we can't read one character.
-unsigned char xread_char(int fd)
+int ndelay_off(int fd)
{
- char tmp;
+ return fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) & ~O_NONBLOCK);
+}
- xread(fd, &tmp, 1);
+void xdup2(int from, int to)
+{
+ if (dup2(from, to) != to)
+ bb_perror_msg_and_die("can't duplicate file descriptor");
+}
- return tmp;
+// "Renumber" opened fd
+void xmove_fd(int from, int to)
+{
+ if (from == to)
+ return;
+ xdup2(from, to);
+ close(from);
}
-// Die with supplied error message if this FILE * has ferror set.
-void xferror(FILE *fp, const char *fn)
+// Die with an error message if we can't write the entire buffer.
+void xwrite(int fd, const void *buf, size_t count)
+{
+ if (count) {
+ ssize_t size = full_write(fd, buf, count);
+ if (size != count)
+ bb_error_msg_and_die("short write");
+ }
+}
+
+// Die with an error message if we can't lseek to the right spot.
+off_t xlseek(int fd, off_t offset, int whence)
+{
+ off_t off = lseek(fd, offset, whence);
+ if (off == (off_t)-1) {
+ if (whence == SEEK_SET)
+ bb_perror_msg_and_die("lseek(%"OFF_FMT"u)", offset);
+ bb_perror_msg_and_die("lseek");
+ }
+ return off;
+}
+
+// Die with supplied filename if this FILE * has ferror set.
+void die_if_ferror(FILE *fp, const char *fn)
{
if (ferror(fp)) {
- bb_error_msg_and_die("%s", fn);
+ /* ferror doesn't set useful errno */
+ bb_error_msg_and_die("%s: I/O error", fn);
}
}
// Die with an error message if stdout has ferror set.
-void xferror_stdout(void)
+void die_if_ferror_stdout(void)
{
- xferror(stdout, bb_msg_standard_output);
+ die_if_ferror(stdout, bb_msg_standard_output);
}
// Die with an error message if we have trouble flushing stdout.
}
}
-// This does a fork/exec in one call, using vfork(). Return PID of new child,
-// -1 for failure. Runs argv[0], searching path if that has no / in it.
-pid_t spawn(char **argv)
+void sig_block(int sig)
{
- static int failed;
- pid_t pid;
- void *app = ENABLE_FEATURE_SH_STANDALONE_SHELL ? find_applet_by_name(argv[0]) : 0;
-
- // Be nice to nommu machines.
- failed = 0;
- pid = vfork();
- if (pid < 0) return pid;
- if (!pid) {
- execvp(app ? CONFIG_BUSYBOX_EXEC_PATH : *argv, argv);
-
- // We're sharing a stack with blocked parent, let parent know we failed
- // and then exit to unblock parent (but don't run atexit() stuff, which
- // would screw up parent.)
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss, sig);
+ sigprocmask(SIG_BLOCK, &ss, NULL);
+}
- failed = -1;
- _exit(0);
- }
- return failed ? failed : pid;
+void sig_unblock(int sig)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss, sig);
+ sigprocmask(SIG_UNBLOCK, &ss, NULL);
}
-// Die with an error message if we can't spawn a child process.
-pid_t xspawn(char **argv)
+#if 0
+void sig_blocknone(void)
{
- pid_t pid = spawn(argv);
- if (pid < 0) bb_perror_msg_and_die("%s", *argv);
- return pid;
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigprocmask(SIG_SETMASK, &ss, NULL);
}
+#endif
-// Wait for the specified child PID to exit, returning child's error return.
-int wait4pid(int pid)
+void sig_catch(int sig, void (*f)(int))
{
- int status;
+ struct sigaction sa;
+ sa.sa_handler = f;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(sig, &sa, NULL);
+}
- if (pid == -1 || waitpid(pid, &status, 0) == -1) return -1;
- if (WIFEXITED(status)) return WEXITSTATUS(status);
- if (WIFSIGNALED(status)) return WTERMSIG(status);
- return 0;
+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);
}
-// 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.
-void utoa_to_buf(unsigned n, char *buf, unsigned buflen)
+/* 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)
{
- int i, out = 0;
- if (buflen) {
- for (i=1000000000; i; i/=10) {
- int res = n/i;
+ const char *fmt;
+ char c;
+ 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";
+ 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[u/10%10];
+ if (c != ' ') fmt = "0123456789";
+ buf[2] = fmt[u%10];
+ buf[3] = "0123456789"[v];
+ } else {
+ // 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";
+ 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];
+ buf[3] = scale[idx]; /* typically scale = " kmgt..." */
+ }
+}
+
+/* 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..." */
+ }
+}
- if ((res || out || i == 1) && --buflen>0) {
+
+// 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);
+char *utoa_to_buf(unsigned n, char *buf, unsigned buflen)
+{
+ unsigned i, out, res;
+ if (sizeof(unsigned) != 4)
+ BUG_sizeof_unsigned_not_4();
+ if (buflen) {
+ out = 0;
+ for (i = 1000000000; i; i /= 10) {
+ res = n / i;
+ if (res || out || i == 1) {
+ if (!--buflen) break;
out++;
n -= res*i;
*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
// 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;
}
// 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;
}
+// Emit a string of hex representation of bytes
+char *bin2hex(char *p, const char *cp, int count)
+{
+ while (count) {
+ unsigned char c = *cp++;
+ /* put lowercase hex digits */
+ *p++ = 0x20 | bb_hexdigits_upcase[c >> 4];
+ *p++ = 0x20 | bb_hexdigits_upcase[c & 0xf];
+ count--;
+ }
+ return p;
+}
+
// Die with an error message if we can't set gid. (Because resource limits may
// limit this user to a given number of processes, and if that fills up the
// 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 cant' set uid. (See xsetgid() for why.)
+// 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.
if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512;
+ // FIXME: explain why lseek(SEEK_END) is not used here!
+
// If not, do a binary search for the last location we can read. (Some
// block devices don't do BLKGETSIZE right.)
// If we can read from the current location, it's bigger.
- if (lseek(fd, pos, 0)>=0 && safe_read(fd, &temp, 1)==1) {
+ if (lseek(fd, pos, SEEK_SET)>=0 && safe_read(fd, &temp, 1)==1) {
if (bottom == top) bottom = top = (top+1) * 2;
else bottom = pos;
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, ...)
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;
}
+#if 0 /* If we will ever meet a libc which hasn't [f]dprintf... */
+int fdprintf(int fd, const char *format, ...)
+{
+ va_list p;
+ int r;
+ char *string_ptr;
+
+#if 1
+ // GNU extension
+ va_start(p, format);
+ r = vasprintf(&string_ptr, format, p);
+ va_end(p);
+#else
+ // Bloat for systems that haven't got the GNU extension.
+ va_start(p, format);
+ 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) {
+ full_write(fd, string_ptr, r);
+ free(string_ptr);
+ }
+ return r;
+}
+#endif
+
// Die with an error message if we can't copy an entire FILE * to stdout, then
// close that file.
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);
}
{
DIR *dp;
- if ((dp = opendir(path)) == NULL) {
- bb_perror_msg("unable to open `%s'", path);
- return NULL;
- }
+ dp = opendir(path);
+ if (!dp)
+ bb_perror_msg("can't open '%s'", path);
return dp;
}
{
DIR *dp;
- if ((dp = opendir(path)) == NULL)
- bb_perror_msg_and_die("unable to open `%s'", path);
+ dp = opendir(path);
+ if (!dp)
+ bb_perror_msg_and_die("can't open '%s'", path);
return dp;
}
-#ifndef BB_NOMMU
-// Die with an error message if we can't daemonize.
-void xdaemon(int nochdir, int noclose)
-{
- if (daemon(nochdir, noclose)) bb_perror_msg_and_die("daemon");
-}
-#endif
-
// Die with an error message if we can't open a new socket.
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;
}
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(char *name, struct stat *stat_buf)
+void xstat(const char *name, struct stat *stat_buf)
{
if (stat(name, stat_buf))
bb_perror_msg_and_die("can't stat '%s'", name);
}
+// selinux_or_die() - die if SELinux is disabled.
+void selinux_or_die(void)
+{
+#if ENABLE_SELINUX
+ int rc = is_selinux_enabled();
+ if (rc == 0) {
+ bb_error_msg_and_die("SELinux is disabled");
+ } else if (rc < 0) {
+ bb_error_msg_and_die("is_selinux_enabled() failed");
+ }
+#else
+ bb_error_msg_and_die("SELinux support is disabled");
+#endif
+}
+
/* It is perfectly ok to pass in a NULL for either width or for
- * * height, in which case that value will not be set. */
+ * height, in which case that value will not be set. */
int get_terminal_width_height(int fd, int *width, int *height)
{
struct winsize win = { 0, 0, 0, 0 };
int ret = ioctl(fd, TIOCGWINSZ, &win);
- if (!win.ws_row) {
- char *s = getenv("LINES");
- if (s) win.ws_row = atoi(s);
+
+ if (height) {
+ if (!win.ws_row) {
+ char *s = getenv("LINES");
+ if (s) win.ws_row = atoi(s);
+ }
+ if (win.ws_row <= 1 || win.ws_row >= 30000)
+ win.ws_row = 24;
+ *height = (int) win.ws_row;
+ }
+
+ if (width) {
+ if (!win.ws_col) {
+ char *s = getenv("COLUMNS");
+ if (s) win.ws_col = atoi(s);
+ }
+ if (win.ws_col <= 1 || win.ws_col >= 30000)
+ win.ws_col = 80;
+ *width = (int) win.ws_col;
}
- if (win.ws_row <= 1) win.ws_row = 24;
- if (!win.ws_col) {
- char *s = getenv("COLUMNS");
- if (s) win.ws_col = atoi(s);
+
+ 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);
}
- if (win.ws_col <= 1) win.ws_col = 80;
- if (height) *height = (int) win.ws_row;
- if (width) *width = (int) win.ws_col;
+ 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