script: new applet by Pascal Bellard <pascal.bellard AT ads-lu.com>
authorDenis Vlasenko <vda.linux@googlemail.com>
Wed, 27 Feb 2008 11:54:59 +0000 (11:54 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Wed, 27 Feb 2008 11:54:59 +0000 (11:54 -0000)
include/applets.h
include/libbb.h
include/usage.h
libbb/Kbuild
miscutils/Config.in
networking/telnetd.c
util-linux/Kbuild
util-linux/script.c [new file with mode: 0644]

index b2e89ee8545e19d2008108b96cea92b65d7d7d25..d4b6dbeaabd0cf02c8a9e32034064638c3174aa1 100644 (file)
@@ -305,6 +305,7 @@ USE_RUNLEVEL(APPLET(runlevel, _BB_DIR_SBIN, _BB_SUID_NEVER))
 USE_RUNSV(APPLET(runsv, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_RUNSVDIR(APPLET(runsvdir, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_SCRIPT(APPLET(script, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
 USE_SENDMAIL(APPLET_ODDNAME(sendmail, sendgetmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, sendmail))
index 978cd2d87d270db4d59d9596f7ac1a6437a707f0..707e8d69bb3008c803bf783c5bafd6bb4c8ea2b6 100644 (file)
@@ -237,6 +237,7 @@ extern int recursive_action(const char *fileName, unsigned flags,
        int (*dirAction) (const char *fileName, struct stat* statbuf, void* userData, int depth),
        void* userData, unsigned depth);
 extern int device_open(const char *device, int mode);
+extern int getpty(char *line, int size);
 extern int get_console_fd(void);
 extern char *find_block_device(const char *path);
 /* bb_copyfd_XX print read/write errors and return -1 if they occur */
index 95cb69c7356d918f96dde97005470b41ea0b2a03..4eac174018bb077c67aa653bcc0613132b9bbe29 100644 (file)
@@ -3234,6 +3234,15 @@ USE_FEATURE_RUN_PARTS_FANCY("\n  -l      Prints names of all matching files even when
 #define rx_example_usage \
        "$ rx /tmp/foo\n"
 
+#define script_trivial_usage \
+       "[-afq] [-c COMMAND] [OUTFILE]"
+#define script_full_usage \
+       "Options:" \
+     "\n       -a      Append output" \
+     "\n       -c      Run COMMAND, not shell" \
+     "\n       -f      Flush output after each write" \
+     "\n       -q      Quiet"
+
 #define sed_trivial_usage \
        "[-efinr] pattern [files...]"
 #define sed_full_usage \
index aab016e850269ef6ea292e659746e8e40c39cfea..fc87f625b0274b0286d54e9a9b07cb51eba739f8 100644 (file)
@@ -39,6 +39,7 @@ lib-y += get_console.o
 lib-y += get_last_path_component.o
 lib-y += get_line_from_file.o
 lib-y += getopt32.o
+lib-y += getpty.o
 lib-y += herror_msg.o
 lib-y += herror_msg_and_die.o
 lib-y += human_readable.o
index 73c765f2ccac7fd7d5aa4235ffb135a0e9bc9afe..ac1e2b57caa0657b0b490d1ee38b6a1cff8ba2b2 100644 (file)
@@ -410,6 +410,12 @@ config RX
        help
          Receive files using the Xmodem protocol.
 
+config SCRIPT
+       bool "script"
+       default n
+       help
+         The script makes typescript of terminal session.
+
 config STRINGS
        bool "strings"
        default n
index 0bffa9700d8565767ffcdb2b232f314417177960..05de49e8a4523b0a868aace2482a93494ef60af8 100644 (file)
@@ -153,53 +153,6 @@ remove_iacs(struct tsession *ts, int *pnum_totty)
 }
 
 
-static int
-getpty(char *line, int size)
-{
-       int p;
-#if ENABLE_FEATURE_DEVPTS
-       p = open("/dev/ptmx", O_RDWR);
-       if (p > 0) {
-               const char *name;
-               grantpt(p);
-               unlockpt(p);
-               name = ptsname(p);
-               if (!name) {
-                       bb_perror_msg("ptsname error (is /dev/pts mounted?)");
-                       return -1;
-               }
-               safe_strncpy(line, name, size);
-               return p;
-       }
-#else
-       struct stat stb;
-       int i;
-       int j;
-
-       strcpy(line, "/dev/ptyXX");
-
-       for (i = 0; i < 16; i++) {
-               line[8] = "pqrstuvwxyzabcde"[i];
-               line[9] = '0';
-               if (stat(line, &stb) < 0) {
-                       continue;
-               }
-               for (j = 0; j < 16; j++) {
-                       line[9] = j < 10 ? j + '0' : j - 10 + 'a';
-                       if (DEBUG)
-                               fprintf(stderr, "Trying to open device: %s\n", line);
-                       p = open(line, O_RDWR | O_NOCTTY);
-                       if (p >= 0) {
-                               line[5] = 't';
-                               return p;
-                       }
-               }
-       }
-#endif /* FEATURE_DEVPTS */
-       return -1;
-}
-
-
 static struct tsession *
 make_new_session(
                USE_FEATURE_TELNETD_STANDALONE(int sock)
index c71186e86be7f933580665b2c0617aa063285f87..0f33c6be210f0c918f3465e4bdec6d98f2de1401 100644 (file)
@@ -28,6 +28,7 @@ lib-$(CONFIG_PIVOT_ROOT)      +=pivot_root.o
 lib-$(CONFIG_RDATE)            +=rdate.o
 lib-$(CONFIG_READPROFILE)      +=readprofile.o
 lib-$(CONFIG_RTCWAKE)          +=rtcwake.o
+lib-$(CONFIG_SCRIPT)           +=script.o
 lib-$(CONFIG_SETARCH)          +=setarch.o
 lib-$(CONFIG_SWAPONOFF)                +=swaponoff.o
 lib-$(CONFIG_SWITCH_ROOT)      +=switch_root.o
diff --git a/util-linux/script.c b/util-linux/script.c
new file mode 100644 (file)
index 0000000..fda726e
--- /dev/null
@@ -0,0 +1,195 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * script implementation for busybox
+ *
+ * pascal.bellard@ads-lu.com
+ *
+ * Based on code from util-linux v 2.12r
+ * Copyright (c) 1980
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file License in this tarball for details.
+ */
+
+#include "libbb.h"
+
+struct globals {
+       int child_pid;
+       int attr_ok; /* NB: 0: ok */
+       struct termios tt;
+       const char *fname;
+};
+#define G (*ptr_to_globals)
+#define child_pid (G.child_pid)
+#define attr_ok   (G.attr_ok  )
+#define tt        (G.tt       )
+#define fname     (G.fname    )
+#define INIT_G() do { \
+       PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
+       fname = "typescript"; \
+} while (0)
+
+static void done(void)
+{
+       if (child_pid) { /* we are parent */
+               if (attr_ok == 0)
+                       tcsetattr(0, TCSAFLUSH, &tt);
+               if (!(option_mask32 & 8)) /* not -q */
+                       printf("Script done, file is %s\n", fname);
+       }
+       exit(0);
+}
+
+#ifdef UNUSED
+static void handle_sigchld(int sig)
+{
+       /* wait for the exited child and exit */
+       while (wait_any_nohang(&sig) > 0)
+               continue;
+       done();
+}
+#endif
+
+#if ENABLE_GETOPT_LONG
+static const char getopt_longopts[] ALIGN1 =
+       "append\0"  No_argument       "a"
+       "command\0" Required_argument "c"
+       "flush\0"   No_argument       "f"
+       "quiet\0"   No_argument       "q"
+       ;
+#endif
+
+int script_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
+int script_main(int argc, char *argv[])
+{
+       int opt, pty;
+       int winsz_ok;
+       int mode;
+       struct termios rtt;
+       struct winsize win;
+       char line[32];
+       const char *shell;
+       char shell_opt[] = "-i";
+       char *shell_arg = NULL;
+
+       INIT_G();
+#if ENABLE_GETOPT_LONG
+       applet_long_options = getopt_longopts;
+#endif
+       opt_complementary = "?1"; /* max one arg */
+       opt = getopt32(argv, "ac:fq", &shell_arg);
+       //argc -= optind;
+       argv += optind;
+       if (argv[0]) {
+               fname = argv[0];
+       }
+       mode = O_CREAT|O_TRUNC|O_WRONLY;
+       if (opt & 1) {
+               mode = O_CREAT|O_APPEND|O_WRONLY;
+       }
+       if (opt & 2) {
+               shell_opt[1] = 'c';
+       }
+       if (!(opt & 8)) { /* not -q */
+               printf("Script started, file is %s\n", fname);
+       }
+       shell = getenv("SHELL");
+       if (shell == NULL) {
+               shell = _PATH_BSHELL;
+       }
+
+       pty = getpty(line, sizeof(line));
+       if (pty < 0) {
+               bb_perror_msg_and_die("can't get pty");
+       }
+
+       /* get current stdin's tty params */
+       attr_ok = tcgetattr(0, &tt);
+       winsz_ok = ioctl(0, TIOCGWINSZ, (char *)&win);
+
+       rtt = tt;
+       cfmakeraw(&rtt);
+       rtt.c_lflag &= ~ECHO;
+       tcsetattr(0, TCSAFLUSH, &rtt);
+
+       /* We exit as soon as child exits */
+       //signal(SIGCHLD, handle_sigchld);
+       signal(SIGCHLD, (void (*)(int)) done);
+
+       child_pid = vfork();
+       if (child_pid < 0) {
+               bb_perror_msg_and_die("vfork");
+       }
+
+       if (child_pid) {
+               /* parent */
+               char buf[256];
+               struct pollfd pfd[2];
+               int outfd;
+               int fd_count = 2;
+               struct pollfd *ppfd = pfd;
+
+               outfd = xopen(fname, mode);
+               pfd[0].fd = 0;
+               pfd[0].events = POLLIN;
+               pfd[1].fd = pty;
+               pfd[1].events = POLLIN;
+               ndelay_on(pty); /* this descriptor is not shared, can do this */
+               /* ndelay_on(0); - NO, stdin can be shared! */
+
+               /* copy stdin to pty master input,
+                * copy pty master output to stdout and file */
+               /* TODO: don't use full_write's, use proper write buffering */
+               while (fd_count && safe_poll(ppfd, fd_count, -1) > 0) {
+                       if (pfd[0].revents) {
+                               int count = safe_read(0, buf, sizeof(buf));
+                               if (count <= 0) {
+                                       /* err/eof: don't read anymore */
+                                       pfd[0].revents = 0;
+                                       ppfd++;
+                                       fd_count--;
+                               } else {
+                                       full_write(pty, buf, count);
+                               }
+                       }
+                       if (pfd[1].revents) {
+                               int count;
+                               errno = 0;
+                               count = safe_read(pty, buf, sizeof(buf));
+                               if (count <= 0 && errno != EAGAIN) {
+                                       /* err/eof: don't read anymore */
+                                       pfd[1].revents = 0;
+                                       fd_count--;
+                               }
+                               if (count > 0) {
+                                       full_write(1, buf, count);
+                                       full_write(outfd, buf, count);
+                                       if (opt & 4) { /* -f */
+                                               fsync(outfd);
+                                       }
+                               }
+                       }
+               }
+               done(); /* does not return */
+       }
+
+       /* child: make pty slave to be input, output, error; run shell */
+       close(pty); /* close pty master */
+       /* open pty slave to fd 0,1,2 */
+       close(0);               
+       xopen(line, O_RDWR); /* uses fd 0 */
+       xdup2(0, 1);
+       xdup2(0, 2);
+       /* copy our original stdin tty's parameters to pty */
+       if (attr_ok == 0)
+               tcsetattr(0, TCSAFLUSH, &tt);
+       if (winsz_ok == 0)
+               ioctl(0, TIOCSWINSZ, (char *)&win);
+       /* set pty as a controlling tty */
+       setsid();
+       ioctl(0, TIOCSCTTY, 0 /* 0: don't forcibly steal */);
+
+       /* signal(SIGCHLD, SIG_DFL); - exec does this for us */
+       execl(shell, shell, shell_opt, shell_arg, NULL);
+       bb_simple_perror_msg_and_die(shell);
+}