* 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)
{
{
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
{
if (from == to)
return;
- if (dup2(from, to) != to)
- bb_perror_msg_and_die("cannot duplicate file descriptor");
+ xdup2(from, to);
close(from);
}
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);
}
}
}
}
-// 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)
*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;
}
// 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.
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;
}
#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) {
fflush(stdout);
// copyfd outputs error messages for us.
if (bb_copyfd_eof(fileno(file), 1) == -1)
- exit(xfunc_error_retval);
+ xfunc_die();
fclose(file);
}
dp = opendir(path);
if (!dp)
- bb_perror_msg("cannot open '%s'", path);
+ bb_perror_msg("can't open '%s'", path);
return dp;
}
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;
}
{
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(const char *name, struct stat *stat_buf)
{
/* 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);
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