1 /* top layer of subversion library to intercept RealPlayer socket and
2 device I/O. --20011101 */
6 #define _LARGEFILE_SOURCE
7 #define _LARGEFILE64_SOURCE
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #include <sys/socket.h>
24 #include <netinet/in.h>
28 #include <X11/extensions/XShm.h>
30 static pthread_mutex_t output_mutex
=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
;
31 static pthread_cond_t event_cond
=PTHREAD_COND_INITIALIZER
;
32 static pthread_mutex_t event_mutex
=PTHREAD_MUTEX_INITIALIZER
;
33 static pthread_cond_t display_cond
=PTHREAD_COND_INITIALIZER
;
34 static pthread_mutex_t display_mutex
=PTHREAD_MUTEX_INITIALIZER
;
36 static int (*libc_open
)(const char *,int,mode_t
);
37 static int (*libc_connect
)(int sockfd
, const struct sockaddr
*serv_addr
,
39 static int (*libc_close
)(int);
40 static size_t (*libc_read
)(int,void *,size_t);
41 static size_t (*libc_write
)(int,const void *,size_t);
42 static int (*libc_readv
)(int,struct iovec
*,int);
43 static int (*libc_writev
)(int,const struct iovec
*,int);
44 static int (*libc_ioctl
)(int,int,void *);
45 static pid_t (*libc_fork
)(void);
47 static int (*xlib_xclose
)(Display
*);
48 static Display
*(*xlib_xopen
)(const char *);
49 static int (*xlib_xdrawsegments
)(Display
*,Drawable
,GC
,XSegment
*,int);
50 static Window (*xlib_xcreatewindow
)(Display
*,Window
,int,int,unsigned int,unsigned int,
51 unsigned int,int,unsigned int,Visual
*,unsigned long,
52 XSetWindowAttributes
*);
53 static int (*xlib_xconfigurewindow
)(Display
*,Window
,unsigned int,XWindowChanges
*);
54 static int (*xlib_xchangeproperty
)(Display
*,Window
,Atom
,Atom
,int,int,const unsigned char *,int);
55 static int (*xlib_xputimage
)(Display
*,Drawable
,GC
,XImage
*,int,int,int,int,
56 unsigned int,unsigned int);
57 static int (*xlib_xshmputimage
)(Display
*,Drawable
,GC
,XImage
*,int,int,int,int,
58 unsigned int,unsigned int,Bool
);
60 static int (*xlib_xresizewindow
)(Display
*,Window
,unsigned int,unsigned int);
62 static Display
*Xdisplay
;
66 static FILE *backchannel_fd
=NULL
;
68 static int audio_fd
=-1;
69 static int audio_channels
=-1;
70 static int audio_rate
=-1;
71 static int audio_format
=-1;
72 static long long audio_samplepos
=0;
73 static double audio_timezero
=0;
75 static pthread_t snatch_backchannel_thread
;
76 static pthread_t snatch_event_thread
;
78 static char *username
=NULL
;
79 static char *password
=NULL
;
80 static char *openfile
=NULL
;
81 static char *location
=NULL
;
83 static int snatch_active
=1;
84 static int fake_audiop
=0;
85 static int fake_videop
=0;
87 static int output_audio_p
=0;
88 static int output_video_p
=0;
90 static void (*QueuedTask
)(void);
92 static int outfile_fd
=-1;
94 static void TempCloseOutputFile();
95 static void CloseOutputFile(int preserve
);
96 static void OpenOutputFile();
98 static char *audio_fmts
[]={"unknown format",
103 "signed, 16 bit, little endian",
104 "signed, 16 bit, big endian",
106 "unsigned, 16 bit, little endian",
107 "unsigned, 16 bit, big endian"};
109 static int audio_fmt_bytesps
[]={0,1,1,0,1,2,2,1,2,2};
111 static char *nstrdup(char *s
){
112 if(s
)return strdup(s
);
116 static int gwrite(int fd
, void *buf
, int n
){
119 int ret
=(*libc_write
)(fd
,buf
,n
);
124 fprintf(stderr
,"**ERROR: Write error on capture file!\n"
125 " : %s\n",strerror(errno
));
126 TempCloseOutputFile(); /* if the error is the 2GB limit on Linux 2.2,
127 this will result in a new file getting opened */
138 static double bigtime(long *seconds
,long *micros
){
139 static struct timeval tp
;
141 (void)gettimeofday(&tp
, (struct timezone
*)NULL
);
142 if(seconds
)*seconds
=tp
.tv_sec
;
143 if(micros
)*micros
=tp
.tv_usec
;
144 return(tp
.tv_sec
+tp
.tv_usec
*.000001);
147 /* yeah, ugly, but I don't want to leak symbols. Oh and I'm lazy. */
152 void *get_me_symbol(char *symbol
){
153 void *ret
=dlsym(RTLD_NEXT
,symbol
);
155 char *dlerr
=dlerror();
157 "**ERROR: libsnatch.so could not find the function '%s()'.\n"
158 " This shouldn't happen and I'm not going to\n"
159 " make any wild guesses as to what caused it. The\n"
160 " error returned by dlsym() was:\n %s",
161 symbol
,(dlerr
?dlerr
:"no such symbol"));
162 fprintf (stderr
,"\n\nCannot continue. exit(1)ing...\n\n");
166 fprintf(stderr
," ...: symbol '%s()' found and subverted.\n",symbol
);
171 void *event_thread(void *dummy
){
173 fprintf(stderr
," ...: Event thread %lx reporting for duty!\n",
174 (unsigned long)pthread_self());
177 pthread_cond_wait(&event_cond
,&event_mutex
);
183 "**ERROR: Internal fault! event thread awoke without an event\n"
189 void *backchannel_thread(void *dummy
){
191 fprintf(stderr
," ...: Backchannel thread %lx reporting for duty!\n",
192 (unsigned long)pthread_self());
194 pthread_mutex_lock(&display_mutex
);
196 pthread_cond_wait(&display_cond
,&display_mutex
);
197 pthread_mutex_unlock(&display_mutex
);
201 size_t ret
=fread(&rq
,1,1,backchannel_fd
);
206 fprintf(stderr
,"**ERROR: Backchannel lost! exit(1)ing...\n");
216 ret
=fread(&sym
,1,1,backchannel_fd
);
217 ret
=fread(&mod
,2,1,backchannel_fd
);
219 FakeKeySym(sym
,mod
,rpplay_window
);
221 CloseOutputFile(0); /* it will only happen on Robot commands that would
222 be starting a new file */
231 ret
=fread(&length
,2,1,backchannel_fd
);
233 if(length
)buf
=calloc(length
+1,1);
234 if(length
)ret
=fread(buf
,1,length
,backchannel_fd
);
235 if(length
&& (int)ret
==length
)
238 if(username
)free(username
);
242 if(password
)free(password
);
246 if(location
)free(location
);
250 if(openfile
)free(openfile
);
254 if(esdsocket
)free(esdsocket
);
258 if(outpath
)free(outpath
);
262 if(ossname
)free(ossname
);
301 void initialize(void){
305 if(getenv("SNATCH_DEBUG"))debug
=1;
306 if(debug
)fprintf(stderr
,
307 "----env: SNATCH_DEBUG\n"
310 /* get handles to the libc symbols we're subverting */
311 libc_open
=get_me_symbol("open");
312 libc_connect
=get_me_symbol("connect");
313 libc_close
=get_me_symbol("close");
314 libc_read
=get_me_symbol("read");
315 libc_write
=get_me_symbol("write");
316 libc_readv
=get_me_symbol("readv");
317 libc_writev
=get_me_symbol("writev");
318 libc_ioctl
=get_me_symbol("ioctl");
319 libc_fork
=get_me_symbol("fork");
320 xlib_xopen
=get_me_symbol("XOpenDisplay");
321 xlib_xclose
=get_me_symbol("XCloseDisplay");
322 xlib_xdrawsegments
=get_me_symbol("XDrawSegments");
323 xlib_xcreatewindow
=get_me_symbol("XCreateWindow");
324 xlib_xconfigurewindow
=get_me_symbol("XConfigureWindow");
325 xlib_xchangeproperty
=get_me_symbol("XChangeProperty");
326 xlib_xputimage
=get_me_symbol("XPutImage");
327 xlib_xshmputimage
=get_me_symbol("XShmPutImage");
328 xlib_xresizewindow
=get_me_symbol("XResizeWindow");
331 outpath
=nstrdup(getenv("SNATCH_OUTPUT_PATH"));
335 "----env: SNATCH_OUTPUT_PATH\n"
336 " not set. Using current working directory.\n");
337 outpath
=nstrdup(".");
341 "----env: SNATCH_OUTPUT_PATH\n"
342 " set (%s)\n",outpath
);
348 if(getenv("SNATCH_AUDIO_FAKE")){
351 "----env: SNATCH_AUDIO_FAKE\n"
352 " set. Faking audio operations.\n");
357 "----env: SNATCH_AUDIO_FAKE\n"
360 if(getenv("SNATCH_VIDEO_FAKE")){
363 "----env: SNATCH_VIDEO_FAKE\n"
364 " set. Faking video operations.\n");
369 "----env: SNATCH_VIDEO_FAKE\n"
373 fprintf(stderr
," ...: Now watching for RealPlayer audio/video output.\n");
378 struct sockaddr_un addr
;
379 char backchannel_socket
[sizeof(addr
.sun_path
)];
382 memset(backchannel_socket
,0,sizeof(backchannel_socket
));
383 if(getenv("SNATCH_COMM_SOCKET"))
384 strncpy(backchannel_socket
,getenv("SNATCH_COMM_SOCKET"),
385 sizeof(addr
.sun_path
)-1);
387 if(backchannel_socket
[0]){
391 "----env: SNATCH_COMM_SOCKET\n"
392 " set to %s; trying to connect...\n",
394 temp_fd
=socket(AF_UNIX
,SOCK_STREAM
,0);
397 "**ERROR: socket() call for backchannel failed.\n"
398 " returned error %d:%s\n"
399 " exit(1)ing...\n\n",errno
,strerror(errno
));
403 addr
.sun_family
=AF_UNIX
;
404 strcpy(addr
.sun_path
,backchannel_socket
);
406 if((*libc_connect
)(temp_fd
,(struct sockaddr
*)&addr
,sizeof(addr
))<0){
408 "**ERROR: connect() call for backchannel failed.\n"
409 " returned error %d: %s\n"
410 " exit(1)ing...\n\n",errno
,strerror(errno
));
415 " ...: connected to backchannel\n");
417 backchannel_fd
=fdopen(temp_fd
,"w+");
418 if(backchannel_fd
==NULL
){
420 "**ERROR: fdopen() failed on backchannel fd. error returned:\n"
421 " %s\n exit(1)ing...\n\n",strerror(errno
));
427 " ...: starting backchannel/fake event threads...\n");
429 if((ret
=pthread_create(&snatch_backchannel_thread
,NULL
,
430 backchannel_thread
,NULL
))){
432 "**ERROR: could not create backchannel worker thread.\n"
433 " Error code returned: %d\n"
434 " exit(1)ing...\n\n",ret
);
437 pthread_mutex_lock(&event_mutex
);
438 if((ret
=pthread_create(&snatch_event_thread
,NULL
,
439 event_thread
,NULL
))){
441 "**ERROR: could not create event worker thread.\n"
442 " Error code returned: %d\n"
443 " exit(1)ing...\n\n",ret
);
450 "----env: SNATCH_COMM_SOCKET\n"
460 return((*libc_fork
)());
463 int open(const char *pathname
,int flags
,...){
469 if(oss_identify(pathname
))return oss_open_hook(pathname
);
473 mode
=va_arg(ap
,mode_t
);
477 return ((*libc_open
)(pathname
,flags
,mode
));
480 int connect(int sockfd
,const struct sockaddr
*serv_addr
,socklen_t addrlen
){
483 if(esd_identify(serv_addr
,addrlen
))
484 return esd_connect_hook(sockfd
,serv_addr
,addrlen
);
486 return ((*libc_connect
)(sockfd
,serv_addr
,addrlen
));
497 ret
=(*libc_close
)(fd
);
503 audio_timezero
=bigtime(NULL
,NULL
);
506 " ...: RealPlayer closed audio playback fd %d\n",fd
);
513 ssize_t
read(int fd
, void *buf
,size_t count
){
514 if(esd_rw_hook_p(fd
))return(esd_read_hook(fd
,buf
,count
));
515 return((*libc_read
)(fd
,buf
,count
));
518 ssize_t
write(int fd
, const void *buf
,size_t count
){
522 /* track audio sync regardless of record activity or fake setting;
523 we could have record suddenly activated */
526 double now
=bigtime(NULL
,NULL
),fa
;
527 double stime
=audio_samplepos
/(double)audio_rate
+audio_timezero
;
529 /* do it first so we know how many bytes actually succeed */
531 count
=(*libc_write
)(fd
,buf
,count
);
532 if(count
<=0)return(count
);
534 /* video and audio buffer differently; video is always 'just
535 in time' and thus uses absolute time positioning. Video
536 frames can also arrive 'late' because of X server
537 congestion, but frames will never arrive early. Sound is
538 the opposite; samples must be queued ahead of time (and
539 will queue arbitrarily deep) and always must arrive early,
540 not late. For this reason, we need to pay attention to when
541 the current sample buffer will begin playing, not when it
545 /* queue starved; player will need to skip or stretch. Advance
546 the absolute position to maintain sync. Note that this is
547 normal at beginning of capture or after pause */
548 audio_samplepos
=(now
-audio_timezero
)*audio_rate
;
549 stime
=audio_samplepos
/(double)audio_rate
+audio_timezero
;
552 b
=modf(stime
,&fa
)*1000000.;
555 if(count
>0 && snatch_active
==1){
557 pthread_mutex_lock(&output_mutex
);
558 if(outfile_fd
>=0 && !output_audio_p
)CloseOutputFile(1);
559 if(outfile_fd
<0)OpenOutputFile();
560 pthread_mutex_unlock(&output_mutex
);
562 if(outfile_fd
>=0){ /* always be careful */
566 len
=sprintf(cbuf
,"AUDIO %ld %ld %d %d %d %d:",a
,b
,audio_channels
,
567 audio_rate
,audio_format
,count
);
569 pthread_mutex_lock(&output_mutex
);
570 gwrite(outfile_fd
,cbuf
,len
);
571 gwrite(outfile_fd
,(void *)buf
,count
);
572 pthread_mutex_unlock(&output_mutex
);
576 audio_samplepos
+=(count
/(audio_channels
*audio_fmt_bytesps
[audio_format
]));
581 if(esd_rw_hook_p(fd
))return(esd_write_hook(fd
,buf
,count
));
583 return((*libc_write
)(fd
,buf
,count
));
586 int writev(int fd
,const struct iovec
*v
,int n
){
589 ret
=write(fd
,v
[i
].iov_base
,v
[i
].iov_len
);
590 if(ret
<0 && count
==0)return(ret
);
591 if(ret
<0)return(count
);
593 if(ret
<(int)(v
[i
].iov_len
))return(count
);
598 int readv(int fd
,const struct iovec
*v
,int n
){
601 ret
=read(fd
,v
[i
].iov_base
,v
[i
].iov_len
);
602 if(ret
<0 && count
==0)return(ret
);
603 if(ret
<0)return(count
);
605 if(ret
<(int)(v
[i
].iov_len
))return(count
);
610 int ioctl(int fd
,unsigned long int rq
, ...){
615 va_start(optional
,rq
);
616 arg
=va_arg(optional
,void *);
619 if(oss_ioctl_hook_p(fd
))return(oss_ioctl_hook(fd
,rq
,arg
));
621 return((*libc_ioctl
)(fd
,rq
,arg
));
624 static void queue_task(void (*f
)(void)){
625 pthread_mutex_lock(&event_mutex
);
627 pthread_cond_signal(&event_cond
);
628 pthread_mutex_unlock(&event_mutex
);
631 static void OpenOutputFile(){
633 if(!strcmp(outpath
,"-")){
634 outfile_fd
=STDOUT_FILENO
;
635 if(debug
)fprintf(stderr
," ...: Capturing to stdout\n");
637 if(videocount
|| output_video_p
){
641 gwrite(outfile_fd
,"SNATCHAV---\n",12);
643 gwrite(outfile_fd
,"SNATCH-V---\n",12);
648 gwrite(outfile_fd
,"SNATCHA----\n",12);
650 gwrite(outfile_fd
,"SNATCH-----\n",12);
656 int ret
=stat(outpath
,&buf
);
657 if(!ret
&& S_ISDIR(buf
.st_mode
)){
658 /* construct a new filename */
664 now
=localtime(&nows
);
665 strftime(buf1
,256,"%Y%m%d_%H:%M:%S",now
);
666 if(videocount
|| output_video_p
){
668 sprintf(buf2
,"%s/%s_%s%dHz_%dx%d_AV.snatch",
671 (audio_channels
==1?"mono":"stereo"),
676 sprintf(buf2
,"%s/%s_%dx%d_V.snatch",
684 sprintf(buf2
,"%s/%s_%s%dHz_A.snatch",
687 (audio_channels
==1?"mono":"stereo"),
694 outfile_fd
=open64(buf2
,O_RDWR
|O_CREAT
|O_APPEND
,0770);
696 fprintf(stderr
,"**ERROR: Could not stat requested output path!\n"
697 " %s: %s\n\n",buf2
,strerror(errno
));
700 if(debug
)fprintf(stderr
," ...: Capturing to file %s\n",buf2
);
703 if(videocount
|| output_video_p
){
707 gwrite(outfile_fd
,"SNATCHAV---\n",12);
709 gwrite(outfile_fd
,"SNATCH-V---\n",12);
714 gwrite(outfile_fd
,"SNATCHA----\n",12);
716 gwrite(outfile_fd
,"SNATCH-----\n",12);
721 outfile_fd
=open64(outpath
,O_RDWR
|O_CREAT
|O_APPEND
,0770);
723 fprintf(stderr
,"**ERROR: Could not stat requested output path!\n"
724 " %s: %s\n\n",outpath
,strerror(errno
));
727 if(debug
)fprintf(stderr
," ...: Capturing to file %s\n",outpath
);
729 if(videocount
|| output_video_p
){
733 gwrite(outfile_fd
,"SNATCHAV---\n",12);
735 gwrite(outfile_fd
,"SNATCH-V---\n",12);
740 gwrite(outfile_fd
,"SNATCHA----\n",12);
742 gwrite(outfile_fd
,"SNATCH-----\n",12);
751 static void CloseOutputFile(int preserve
){
752 pthread_mutex_lock(&output_mutex
);
755 if(debug
)fprintf(stderr
," ...: Capture stopped.\n");
756 if(outfile_fd
!=STDOUT_FILENO
)
764 pthread_mutex_unlock(&output_mutex
);
767 static void TempCloseOutputFile(void){
768 pthread_mutex_lock(&output_mutex
);
770 if(debug
)fprintf(stderr
," ...: Capture file closed; trying to continue...\n");
771 if(outfile_fd
!=STDOUT_FILENO
)
775 pthread_mutex_unlock(&output_mutex
);