00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "defs.h"
00022 #include "ptutil.h"
00023 #include "tc_setraw.h"
00024 #include <sys/time.h>
00025 #include <termios.h>
00026
00027
00028
00029
00030
00031 static void timeval_subtract(const struct timeval *x,
00032 const struct timeval *y, struct timeval *diff)
00033 {
00034 if (x->tv_sec == y->tv_sec || x->tv_usec >= y->tv_usec) {
00035 diff->tv_sec = x->tv_sec - y->tv_sec;
00036 diff->tv_usec = x->tv_usec - y->tv_usec;
00037 }
00038 else {
00039 diff->tv_sec = x->tv_sec - 1 - y->tv_sec;
00040 diff->tv_usec = 1000000 + x->tv_usec - y->tv_usec;
00041 }
00042 }
00043
00044 static void get_rel_time(struct timeval *tv_rel)
00045 {
00046 static bool first = true;
00047 static struct timeval starttime;
00048 struct timeval tv;
00049
00050 if (first) {
00051 first = false;
00052 (void)gettimeofday(&starttime, NULL);
00053 }
00054 (void)gettimeofday(&tv, NULL);
00055 timeval_subtract(&tv, &starttime, tv_rel);
00056 }
00057
00058 #define EVFILE "recording.tmp"
00059 #define EVBUFSIZE 512
00060
00061 struct event {
00062 struct timeval e_time;
00063 unsigned e_datalen;
00064 };
00065 static int fd_ev = -1;
00066
00067 static bool ev_open(void)
00068 {
00069 ec_neg1( fd_ev = open(EVFILE, O_RDONLY) )
00070 return true;
00071
00072 EC_CLEANUP_BGN
00073 return false;
00074 EC_CLEANUP_END
00075 }
00076
00077 static bool ev_creat(void)
00078 {
00079 ec_neg1( fd_ev = open(EVFILE, O_WRONLY | O_CREAT | O_TRUNC,
00080 PERM_FILE) )
00081 return true;
00082
00083 EC_CLEANUP_BGN
00084 return false;
00085 EC_CLEANUP_END
00086 }
00087
00088 static bool ev_write(char *data, unsigned datalen)
00089 {
00090 struct event ev = { { 0 } };
00091
00092 get_rel_time(&ev.e_time);
00093 ev.e_datalen = datalen;
00094 ec_neg1( writeall(fd_ev, &ev, sizeof(ev)) )
00095 ec_neg1( writeall(fd_ev, data, datalen) )
00096 return true;
00097
00098 EC_CLEANUP_BGN
00099 return false;
00100 EC_CLEANUP_END
00101 }
00102
00103 static bool ev_read(struct event *ev, char *data, unsigned datalen)
00104 {
00105 ssize_t nread;
00106
00107 ec_neg1( nread = read(fd_ev, ev, sizeof(*ev)) )
00108 if (nread != sizeof(*ev)) {
00109 errno = EIO;
00110 EC_FAIL
00111 }
00112 ec_neg1( nread = read(fd_ev, data, ev->e_datalen) )
00113 if (nread != ev->e_datalen) {
00114 errno = EIO;
00115 EC_FAIL
00116 }
00117 return true;
00118
00119 EC_CLEANUP_BGN
00120 return false;
00121 EC_CLEANUP_END
00122 }
00123
00124 static void ev_close(void)
00125 {
00126 (void)close(fd_ev);
00127 fd_ev = -1;
00128 }
00129
00130 static bool ev_sleep(struct timeval *tv)
00131 {
00132 struct timeval tv_rel, tv_diff;
00133
00134 get_rel_time(&tv_rel);
00135 if (tv->tv_sec > tv_rel.tv_sec || (tv->tv_sec == tv_rel.tv_sec && tv->tv_usec >= tv_rel.tv_usec)) {
00136 timeval_subtract(tv, &tv_rel, &tv_diff);
00137 (void)sleep(tv_diff.tv_sec);
00138 ec_neg1( usleep(tv_diff.tv_usec) )
00139 }
00140
00141 return true;
00142
00143 EC_CLEANUP_BGN
00144 return false;
00145 EC_CLEANUP_END
00146 }
00147
00148 static bool playback(void)
00149 {
00150 bool ok = false;
00151 struct event ev;
00152 char buf[EVBUFSIZE];
00153 struct termios tbuf, tbufsave;
00154
00155 ec_neg1( tcgetattr(STDIN_FILENO, &tbuf) )
00156 tbufsave = tbuf;
00157 tbuf.c_lflag &= ~ECHO;
00158 ec_neg1( tcsetattr(STDIN_FILENO, TCSAFLUSH, &tbuf) )
00159 ec_false( ev_open() )
00160 while (true) {
00161 ec_false( ev_read(&ev, buf, sizeof(buf)) )
00162 if (ev.e_datalen == 0)
00163 break;
00164 ev_sleep(&ev.e_time);
00165 ec_neg1( writeall(STDOUT_FILENO, buf, ev.e_datalen) )
00166 }
00167 ec_neg1( write(STDOUT_FILENO, "\n", 1) )
00168 ok = true;
00169 EC_CLEANUP
00170
00171 EC_CLEANUP_BGN
00172 (void)tcdrain(STDOUT_FILENO);
00173 (void)sleep(1);
00174 (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tbufsave);
00175 ev_close();
00176 return ok;
00177 EC_CLEANUP_END
00178 }
00179
00180
00181
00182
00183
00184 bool exec_redirected(const char *file, char *const argv[], int fd_stdin,
00185 int fd_stdout, int fd_stderr)
00186 {
00187 if (fd_stdin != STDIN_FILENO)
00188 ec_neg1( dup2(fd_stdin, STDIN_FILENO) )
00189 if (fd_stdout != STDOUT_FILENO)
00190 ec_neg1( dup2(fd_stdout, STDOUT_FILENO) )
00191 if (fd_stderr != STDERR_FILENO)
00192 ec_neg1( dup2(fd_stderr, STDERR_FILENO) )
00193 if (fd_stdin != STDIN_FILENO)
00194 (void)close(fd_stdin);
00195 if (fd_stdout != STDOUT_FILENO)
00196 (void)close(fd_stdout);
00197 if (fd_stderr != STDERR_FILENO)
00198 (void)close(fd_stderr);
00199 ec_neg1( execvp(file, argv) )
00200
00201 EC_CLEANUP_BGN
00202 return false;
00203 EC_CLEANUP_END
00204 }
00205
00206 int main(int argc, char *argv[])
00207 {
00208 bool ok = false;
00209 PTINFO *p = NULL;
00210
00211 if (argc < 2) {
00212 fprintf(stderr, "Usage: record cmd ...\n record -p\n");
00213 exit(EXIT_FAILURE);
00214 }
00215 if (strcmp(argv[1], "-p") == 0) {
00216 playback();
00217 ok = true;
00218 EC_CLEANUP
00219 }
00220
00221 ec_null( p = pt_open_master() )
00222 switch (fork()) {
00223 case 0:
00224 ec_false( pt_open_slave(p) )
00225 ec_false( exec_redirected(argv[1], &argv[1], PT_GET_SLAVE_FD(p),
00226 PT_GET_SLAVE_FD(p), PT_GET_SLAVE_FD(p)) )
00227 break;
00228 case -1:
00229 EC_FAIL
00230 }
00231 ec_false( ev_creat() )
00232 ec_false( pt_wait_master(p) )
00233
00234 tc_setraw();
00235 while (true) {
00236 fd_set fd_set_read;
00237 char buf[EVBUFSIZE];
00238 ssize_t nread;
00239
00240 FD_ZERO(&fd_set_read);
00241 FD_SET(STDIN_FILENO, &fd_set_read);
00242 FD_SET(PT_GET_MASTER_FD(p), &fd_set_read);
00243 ec_neg1( select(FD_SETSIZE, &fd_set_read, NULL, NULL, NULL) )
00244 if (FD_ISSET(STDIN_FILENO, &fd_set_read)) {
00245 ec_neg1( nread = read(STDIN_FILENO, &buf, sizeof(buf)) )
00246 ec_neg1( writeall(PT_GET_MASTER_FD(p), buf, nread) )
00247 }
00248 if (FD_ISSET(PT_GET_MASTER_FD(p), &fd_set_read)) {
00249 if ((nread = read(PT_GET_MASTER_FD(p), &buf,
00250 sizeof(buf))) > 0) {
00251 ec_false( ev_write(buf, nread) )
00252 ec_neg1( writeall(STDOUT_FILENO, buf, nread) )
00253 }
00254 else if (nread == 0 || (nread == -1 && errno == EIO))
00255 break;
00256 else
00257 EC_FAIL
00258 }
00259 }
00260 ec_neg1( ev_write(NULL, 0) )
00261 fprintf(stderr,
00262 "EOF or error reading stdin or master pseudo-terminal; exiting\n");
00263 ok = true;
00264 EC_CLEANUP
00265
00266 EC_CLEANUP_BGN
00267 if (p != NULL)
00268 (void)pt_close_master(p);
00269 tc_restore();
00270 ev_close();
00271 printf("\n");
00272 exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
00273 EC_CLEANUP_END
00274 }
00275