Introduce FEATURE_EXEC_PREFER_APPLETS = "re-execute our own
[oweals/busybox.git] / libbb / xfuncs.c
index 352515af40da8c8b5374d7d5bc0a5d1c3935064f..4252e7646bd7c29e8ee10d9c67c53e908fe0d175 100644 (file)
@@ -57,7 +57,7 @@ char * xstrdup(const char *s)
        if (s == NULL)
                return NULL;
 
-       t = strdup (s);
+       t = strdup(s);
 
        if (t == NULL)
                bb_error_msg_and_die(bb_msg_memory_exhausted);
@@ -69,22 +69,33 @@ char * xstrdup(const char *s)
 // 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 = 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)
+       FILE *fp = fopen(path, mode);
+       if (fp == NULL)
                bb_perror_msg_and_die("%s", path);
        return fp;
 }
@@ -92,8 +103,8 @@ FILE *xfopen(const char *path, const char *mode)
 // Die if we can't open an existing file and return an fd.
 int xopen(const char *pathname, int flags)
 {
-       if (ENABLE_DEBUG && (flags & O_CREAT))
-               bb_error_msg_and_die("xopen() with O_CREAT");
+       //if (ENABLE_DEBUG && (flags & O_CREAT))
+       //      bb_error_msg_and_die("xopen() with O_CREAT");
 
        return xopen3(pathname, flags, 0666);
 }
@@ -110,8 +121,19 @@ int xopen3(const char *pathname, int flags, int mode)
        return ret;
 }
 
+// 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);
+}
+
+int ndelay_off(int fd)
+{
+       return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
+}
+
 // Die with an error message if we can't write the entire buffer.
-void xwrite(int fd, void *buf, size_t count)
+void xwrite(int fd, const void *buf, size_t count)
 {
        if (count) {
                ssize_t size = full_write(fd, buf, count);
@@ -124,16 +146,19 @@ void xwrite(int fd, void *buf, size_t count)
 off_t xlseek(int fd, off_t offset, int whence)
 {
        off_t off = lseek(fd, offset, whence);
-       if (off == (off_t)-1)
+       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 error message if this FILE * has ferror set.
+// 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);
+               bb_error_msg_and_die("%s: I/O error", fn);
        }
 }
 
@@ -155,25 +180,33 @@ void xfflush_stdout(void)
 // -1 for failure.  Runs argv[0], searching path if that has no / in it.
 pid_t spawn(char **argv)
 {
+       /* Why static? */
        static int failed;
        pid_t pid;
-       void *app = ENABLE_FEATURE_SH_STANDALONE_SHELL ? find_applet_by_name(argv[0]) : 0;
+       char *prog;
 
        // Be nice to nommu machines.
        failed = 0;
        pid = vfork();
        if (pid < 0) return pid;
        if (!pid) {
-               execvp(app ? CONFIG_BUSYBOX_EXEC_PATH : *argv, argv);
+               prog = argv[0];
+               if (ENABLE_FEATURE_EXEC_PREFER_APPLETS && find_applet_by_name(prog))
+                       prog = CONFIG_BUSYBOX_EXEC_PATH;
+               execvp(prog, 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.)
 
-               failed = -1;
+               failed = errno;
                _exit(0);
        }
-       return failed ? failed : pid;
+       if (failed) {
+               errno = failed;
+               return -1;
+       }
+       return pid;
 }
 
 // Die with an error message if we can't spawn a child process.
@@ -201,7 +234,6 @@ void xsetenv(const char *key, const char *value)
                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'
@@ -244,7 +276,6 @@ void smart_ulltoa5(unsigned long long ul, char buf[5])
        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.
@@ -305,6 +336,19 @@ char *itoa(int n)
        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++ = 0x10 | bb_hexdigits_upcase[c >> 4];
+               *p++ = 0x10 | 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.)
@@ -387,6 +431,37 @@ char *xasprintf(const char *format, ...)
        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);
+       va_end(p);
+       string_ptr = xmalloc(r+1);
+       va_start(p, format);
+       r = vsnprintf(string_ptr, r+1, 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)
@@ -412,7 +487,7 @@ DIR *warn_opendir(const char *path)
        DIR *dp;
 
        if ((dp = opendir(path)) == NULL) {
-               bb_perror_msg("unable to open `%s'", path);
+               bb_perror_msg("cannot open '%s'", path);
                return NULL;
        }
        return dp;
@@ -424,7 +499,7 @@ DIR *xopendir(const char *path)
        DIR *dp;
 
        if ((dp = opendir(path)) == NULL)
-               bb_perror_msg_and_die("unable to open `%s'", path);
+               bb_perror_msg_and_die("cannot open '%s'", path);
        return dp;
 }
 
@@ -437,6 +512,38 @@ void xdaemon(int nochdir, int noclose)
 }
 #endif
 
+void bb_sanitize_stdio_maybe_daemonize(int daemonize)
+{
+       int fd;
+       /* Mega-paranoid */
+       fd = xopen(bb_dev_null, O_RDWR);
+       while (fd < 2)
+               fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
+       if (daemonize) {
+               pid_t pid = fork();
+               if (pid < 0) /* wtf? */
+                       bb_perror_msg_and_die("fork");
+               if (pid) /* parent */
+                       exit(0);
+               /* child */
+               /* if daemonizing, make sure we detach from stdio */
+               setsid();
+               dup2(fd, 0);
+               dup2(fd, 1);
+               dup2(fd, 2);
+       }
+       while (fd > 2)
+               close(fd--); /* close everything after fd#2 */
+}
+void bb_sanitize_stdio(void)
+{
+       bb_sanitize_stdio_maybe_daemonize(0);
+}
+void bb_daemonize(void)
+{
+       bb_sanitize_stdio_maybe_daemonize(1);
+}
+
 // Die with an error message if we can't open a new socket.
 int xsocket(int domain, int type, int protocol)
 {
@@ -468,7 +575,7 @@ void xstat(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(int fd, int *width, int *height)
+int get_terminal_width_height(const int fd, int *width, int *height)
 {
        struct winsize win = { 0, 0, 0, 0 };
        int ret = ioctl(fd, TIOCGWINSZ, &win);