File ttrtail.c of Package ttrtail
/*
* vim:et:
* */
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
/* compile with -DUSER_NAME=\"yourname\" to provide default user name.
you should also consider -DPASSWORD=\"something\" , because default is not
very secure */
#ifndef USER_NAME
#define USER_NAME NULL
#endif
#ifndef PASSWORD
#define PASSWORD "fredfred"
#endif
/* we don't need this, hopefully */
#ifdef NO_UINT32
#define uint32_t long
#endif
/* settings */
char *base_path = "." ;
char *base_ext = "ttyrec" ;
char *server_addr = "213.184.131.118" ; /* default server address */
unsigned short server_port = 31337 ; /* default server port */
char *my_name = USER_NAME ;
char *my_pw = PASSWORD ;
/* global state */
char *curr_path = NULL ;
size_t curr_pos = 0 ;
int curr_fd = 0 ;
int server_sock = 0 ;
int watchers = 0 ;
int most_watchers = 0 ;
/* aux */
char frame[10240] ;
int frame_size ;
char init1[3] ;
char init2[3] ;
void print_msg(const char *fmt, ...) {
va_list vl ;
va_start(vl, fmt);
char buffer[256] ;
time_t curtime ;
struct tm *loctime ;
curtime = time(NULL) ;
loctime = localtime(&curtime) ;
strftime(buffer, 256, "%T ", loctime) ;
printf(buffer) ;
vprintf(fmt, vl) ;
va_end(vl);
}
void die(const char *fmt, ...) {
va_list vl ;
va_start(vl, fmt) ;
vfprintf(stderr, fmt, vl) ;
va_end(vl) ;
exit(-1) ;
}
void die2(const char *fmt, ...) {
va_list vl ;
va_start(vl, fmt) ;
vfprintf(stderr, fmt, vl) ;
fprintf(stderr, ": %s\n", strerror(errno)) ;
va_end(vl) ;
exit(-1) ;
}
void session_close(void) {
print_msg("closing session.\n") ;
if (curr_path) {
free(curr_path) ;
curr_path = NULL ;
}
if (server_sock) {
close(server_sock) ;
server_sock = 0 ;
}
if (curr_fd) {
close(curr_fd) ;
curr_fd = 0 ;
curr_pos = 0 ;
}
}
uint32_t nothtonl(uint32_t x) {
unsigned char *p = (void *)&x ;
return ((((p[3] << 8) | p[2]) << 8) | p[1] << 8) | p[0] ;
}
int read_frame(void) {
uint32_t hdr[3] ;
int st = read(curr_fd, hdr, 12);
int pos = curr_pos ;
static int nfails = 0 ;
if (st == -1) die2("frame header read()") ;
if (st < 12) {
if (st && lseek(curr_fd, curr_pos, SEEK_SET) == -1)
die2("lseek1 (%d)\n", curr_pos) ;
return 0 ;
}
pos += 12 ;
hdr[2] = nothtonl(hdr[2]) ;
if (hdr[2] <= 0 || hdr[2] > sizeof(frame)) {
if (nfails ++ > 100)
die("huh? not trying to read %d bytes\n", hdr[2]) ;
else {
if (st && lseek(curr_fd, curr_pos, SEEK_SET) == -1)
die2("lseek1 (%d)\n", curr_pos) ;
return 0 ;
}
}
nfails = 0 ;
st = read(curr_fd, frame, hdr[2]) ;
if (st == -1) die2("frame data read()") ;
if (st != hdr[2]) {
if (st && lseek(curr_fd, curr_pos, SEEK_SET) == -1) die2("lseek2") ;
return 0 ;
}
pos += st ;
curr_pos = pos ;
return frame_size = st ;
}
char *memstr(char *mem, size_t len, const char *str) {
int lstr = strlen(str) ;
char *p ;
while ((p = memchr(mem, *str, len))) {
if ((len - (mem - p)) < lstr) return NULL ;
if (strncmp(p, str, lstr) == 0) return p ;
len -= (mem - p) + 1 ;
mem = p + 1 ;
}
return NULL ;
}
void skip_some(void) {
size_t best_pos = curr_pos, pos = curr_pos ;
char *p ;
memset(init1, 0, 3) ;
memset(init2, 0, 3) ;
print_msg("skipping some... ") ;
while (read_frame()) {
if ((p = memstr(frame, frame_size, "\033)"))) {
memcpy(init1, p, 3) ;
} else if ((p = memstr(frame, frame_size, "\033("))) {
memcpy(init2, p, 3) ;
}
if (memstr(frame, frame_size, "\033[2J")) {
best_pos = pos ;
}
pos = curr_pos ;
}
print_msg("(%d bytes)\n", best_pos) ;
if (lseek(curr_fd, best_pos, SEEK_SET) == -1) die2("lseek3") ;
curr_pos = best_pos ;
}
void session_open(const char *fname) {
struct sockaddr_in sai ;
char hello[128] ;
size_t hlen ;
curr_fd = open(fname, O_RDONLY) ;
if (curr_fd == -1) die2("open(`%s')", fname) ;
curr_path = strdup(fname) ;
curr_pos = 0 ;
watchers = 0 ;
print_msg("using %s\n", fname);
skip_some() ;
print_msg("connecting to %s:%d...\n", server_addr, server_port) ;
server_sock = socket(PF_INET, SOCK_STREAM, 0) ;
if (server_sock == -1) die2("socket") ;
sai.sin_family = AF_INET;
sai.sin_port = htons(server_port);
sai.sin_addr.s_addr = inet_addr(server_addr) ;
if (connect(server_sock, (void *)&sai, sizeof(sai)) == -1) {
print_msg("connect(%s:%d): %s\nthrottling...\n",
server_addr, server_port, strerror(errno)) ;
sleep(5) ;
session_close() ;
return ;
}
hlen = snprintf(hello, sizeof(hello) - 1,
"hello %.16s %.16s\n", my_name, my_pw) ;
if (write(server_sock, hello, hlen) != hlen) die2("send()") ;
if (*init1 && write(server_sock, init1, 3) != 3) die2("send()") ;
if (*init2 && write(server_sock, init2, 3) != 3) die2("send()") ;
}
int scan_dir(void) {
DIR *d ;
time_t best_time = 0 ;
size_t best_size = 0 ;
struct dirent *de ;
struct stat st ;
char fullpath[PATH_MAX], best[PATH_MAX] ;
print_msg("scanning '%s' for a new ttyrec\n", base_path) ;
*best = 0 ;
d = opendir(base_path) ;
if (!d) die2("opendir(`%s')", base_path) ;
while ((de = readdir(d))) {
if (*de->d_name == '.') continue ;
if (base_ext) {
char *p = strrchr(de->d_name, '.') ;
if (!p || !(* ++p)) continue ;
if (strcmp(p, base_ext)) continue ;
}
snprintf(fullpath, PATH_MAX - 1, "%s/%s", base_path, de->d_name) ;
if (stat(fullpath, &st) == -1) {
fprintf(stderr, "??? stat(`%s'): %s\n", fullpath, strerror(errno)) ;
continue ;
}
if (!S_ISREG(st.st_mode)) continue ;
if (st.st_mtime <= best_time) continue ;
strcpy(best, fullpath) ;
best_time = st.st_mtime ;
best_size = st.st_size ;
}
closedir(d) ;
if (*best) {
if (curr_fd) {
if (strcmp(curr_path, best) == 0 && best_size >= curr_pos)
return 0 ;
session_close() ;
}
session_open(best) ;
} else if (curr_path)
session_close() ;
return 1 ;
}
char *sleep_read(int sex, int usex) {
fd_set rfds ;
fd_set efds ;
struct timeval tv ;
int rv ;
static char s[1024] ;
FD_ZERO(&rfds) ;
FD_ZERO(&efds) ;
FD_SET(server_sock, &rfds) ;
FD_SET(server_sock, &efds) ;
tv.tv_sec = sex ;
tv.tv_usec = usex ;
rv = select(server_sock + 1, &rfds, NULL, &efds, &tv) ;
if (rv == -1) die2("select") ;
if (rv) {
int st ;
if (FD_ISSET(server_sock, &efds)) {
print_msg("socket error\n") ;
session_close() ;
return NULL ;
}
if (!FD_ISSET(server_sock, &rfds)) return NULL ;
memset(s, 0, sizeof(s)) ;
st = read(server_sock, s, sizeof(s) - 1) ;
if (st > 0) return s ;
print_msg("problems with server socket, restarting\n") ;
session_close() ;
}
return NULL ;
}
int main(int ac, char **av) {
unsigned n = 0 ;
if (ac == 1 && my_name == NULL) {
die(
"usage: %s <username> [<password>] [<server ip>] [<server port>]\n"
" <username> parameter is mandatory, others are optional,\n"
" defaults will be used if unspecified.\n"
" default password '%s'.\n"
" default server ip is '%s'. you must specify numeric ip, sorry.\n"
" default server port is %d.\n"
"(if you want to change these defaults, "
"read the comments in ttrtail.c)\n",
*av, my_pw, server_addr, server_port) ;
}
if (ac >= 2) my_name = av[1] ;
if (ac >= 3) my_pw = av[2] ;
if (ac >= 4) server_addr = av[3] ;
if (ac >= 5) server_port = atoi(av[4]) ;
print_msg("username '%s', server %s:%d\n", my_name, server_addr, server_port) ;
for (;;) {
if (!curr_fd || n++ > 100) {
n = 0 ;
scan_dir() ;
}
if (curr_fd && read_frame()) {
n = 0 ;
if (write(server_sock, frame, frame_size) != frame_size)
session_close() ;
} else if (server_sock) {
char *s = sleep_read(0, 200000) ;
if (s) {
if (!strcmp(s, "msg watcher connected\n")) {
++ watchers ;
print_msg("New watcher! Up to %d.", watchers) ;
if (watchers > most_watchers) {
most_watchers = watchers ;
printf(
" That's a new record since "
"this session has started!") ;
}
printf("\n") ;
} else if (!strcmp(s, "msg watcher disconnected\n")) {
-- watchers ;
print_msg("Lost a watcher. Down to %d.\n", watchers) ;
} else
print_msg(">> %s", s) ;
}
} else {
sleep(5) ;
n = 100 ;
}
}
return 1 ;
}