1 /*
2 * A spy program to reveal X11 traffic
3 *
4 * James Peterson, 1988
5 *
6 * Copyright (C) 1988 MCC
7 *
8 * Permission to use, copy, modify, distribute, and sell this software and its
9 * documentation for any purpose is hereby granted without fee, provided that
10 * the above copyright notice appear in all copies and that both that
11 * copyright notice and this permission notice appear in supporting
12 * documentation, and that the name of MCC not be used in
13 * advertising or publicity pertaining to distribution of the software without
14 * specific, written prior permission. MCC makes no
15 * representations about the suitability of this software for any purpose. It
16 * is provided "as is" without express or implied warranty.
17 *
18 * MCC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20 * EVENT SHALL MCC BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
23 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
24 * PERFORMANCE OF THIS SOFTWARE.
25 *
26 */
27 /*
28 * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
29 *
30 * Permission is hereby granted, free of charge, to any person obtaining a
31 * copy of this software and associated documentation files (the "Software"),
32 * to deal in the Software without restriction, including without limitation
33 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
34 * and/or sell copies of the Software, and to permit persons to whom the
35 * Software is furnished to do so, subject to the following conditions:
36 *
37 * The above copyright notice and this permission notice (including the next
38 * paragraph) shall be included in all copies or substantial portions of the
39 * Software.
40 *
41 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
44 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
46 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
47 * DEALINGS IN THE SOFTWARE.
48 *
49 */
50
51 #include "scope.h"
52 #include "nas.h"
53 #include "extensions.h"
54
55 #include <ctype.h>
56 #include <unistd.h>
57
58 #include <sys/types.h> /* needed by sys/socket.h and netinet/in.h */
59 #include <sys/uio.h> /* for struct iovec, used by socket.h */
60 #include <sys/socket.h> /* for AF_INET, SOCK_STREAM, ... */
61 #include <sys/ioctl.h> /* for FIONCLEX, FIONBIO, ... */
62 #if !defined(FIOCLEX) && defined(HAVE_SYS_FILIO_H)
63 #include <sys/filio.h>
64 #endif
65 #include <fcntl.h>
66 #include <netinet/in.h> /* struct sockaddr_in */
67 #include <netinet/tcp.h>
68 #include <netdb.h> /* struct servent * and struct hostent * */
69 #include <errno.h> /* for EINTR, EADDRINUSE, ... */
70
71 #include <locale.h>
72 #ifdef HAVE_LANGINFO_H
73 #include <langinfo.h>
74 #endif
75
76
77 /* ********************************************** */
78 /* */
79 /* ********************************************** */
80
81 #define DefaultPort 6000
82
83 char ServerHostName[MAXHOSTNAMELEN];
84 char AudioServerHostName[MAXHOSTNAMELEN];
85 long ServerBasePort = DefaultPort;
86 static long ServerInPort = 1;
87 static long ServerOutPort = 0;
88 static long ServerDisplay = 0;
89 static char *RawFile;
90
91 static FD ConnectToClient(FD ConnectionSocket);
92 static void DataFromClient(FD fd);
93 static void SetUpStdin(void);
94 static void SetUpRawFile(const char *filename);
95
96 char TranslateText = 0;
97 char IsUTF8locale = 0;
98 char ScopeEnabled = 1;
99 char HandleSIGUSR1 = 0;
100 char DoAudio = 0;
101 char TerminateClose = 0;
102 int Interrupt = 0;
103
104 struct FDDescriptor *FDD = NULL;
105 int MaxFD = 0;
106 int nFDsInUse = 0;
107 fd_set ReadDescriptors;
108 fd_set WriteDescriptors;
109 fd_set BlockedReadDescriptors;
110 int HighestFD;
111
112 short debuglevel = 0;
113 short Verbose = 0;
114 short XVerbose = 0;
115 short NasVerbose = 0;
116 short Raw = 0;
117 int ScopePort = 0;
118 char *ScopeHost = NULL;
119
120
121 typedef struct _BreakPoint {
122 struct _BreakPoint *next;
123 int number;
124 unsigned char request;
125 int minorop;
126 Boolean enabled;
127 } BP;
128
129 typedef enum _CMDResult { CMDProceed, CMDDebug, CMDSyntax, CMDError } CMDResult;
130
131 static CMDResult CMDStep(int argc, char **argv);
132 static CMDResult CMDCont(int argc, char **argv);
133 static CMDResult CMDBreak(int argc, char **argv);
134 static CMDResult CMDDelete(int argc, char **argv);
135 static CMDResult CMDDisable(int argc, char **argv);
136 static CMDResult CMDEnable(int argc, char **argv);
137 static CMDResult CMDLevel(int argc, char **argv);
138 static CMDResult CMDAudio(int argc, char **argv);
139 static CMDResult CMDQuit(int argc, char **argv) _X_NORETURN;
140 static CMDResult CMDHelp(int argc, char **argv);
141
142 typedef struct _CMDFunc {
143 const char *name;
144 const char *alias;
145 CMDResult(*func) (int argc, char **argv);
146 const char *usage;
147 const char *help;
148 } CMDFuncRec;
149 typedef const CMDFuncRec *CMDFuncPtr;
150
151 static const CMDFuncRec CMDFuncs[] = {
152 {"audio", "a", CMDAudio, "[a]udio",
153 "Set audio output level\n"},
154 {"break", "b", CMDBreak, "[b]reak",
155 "Create breakpoints\n"},
156 {"cont", "c", CMDCont, "[c]ont",
157 "Continue scoping\n"},
158 {"delete", "del", CMDDelete, "[del]ete",
159 "Delete breakpoints\n"},
160 {"disable", "d", CMDDisable, "[d]isable",
161 "Disable breakpoints\n"},
162 {"enable", "e", CMDEnable, "[e]nable",
163 "Enable breakpoints\n"},
164 {"help", "?", CMDHelp, "help",
165 "Print this list\n"},
166 {"level", "l", CMDLevel, "[l]evel",
167 "Set output level\n"},
168 {"quit", "q", CMDQuit, "[q]uit",
169 "Quit Xscope\n"},
170 {"step", "s", CMDStep, "[s]tep",
171 "Step trace one request\n"},
172 };
173
174 #define NumCMDFuncs (sizeof CMDFuncs / sizeof CMDFuncs[0])
175
176 #ifndef FALSE
177 #define FALSE 0
178 #define TRUE 1
179 #endif
180
181 static int
CMDStringToInt(char * s,int * v)182 CMDStringToInt(char *s, int *v)
183 {
184 int sign = 1;
185
186 switch (*s) {
187 case '-':
188 sign = -1;
189 s++;
190 break;
191 case '+':
192 s++;
193 break;
194 }
195
196 if (!strncmp(s, "0x", 2)) {
197 sscanf(s + 2, "%x", v);
198 }
199 else if (*s == '0') {
200 sscanf(s, "%o", v);
201 }
202 else if (isdigit(*s)) {
203 sscanf(s, "%d", v);
204 }
205 else if (*s == '\'') {
206 s++;
207 if (*s == '\\') {
208 s++;
209 switch (*s) {
210 case 'n':
211 *v = '\n';
212 break;
213 case 't':
214 *v = '\t';
215 break;
216 default:
217 *v = (int) *s;
218 break;
219 }
220 }
221 else
222 *v = (int) *s;
223 s++;
224 if (*s != '\'')
225 return FALSE;
226 }
227 else
228 return FALSE;
229 *v *= sign;
230 return TRUE;
231 }
232
233 static CMDFuncPtr
CMDStringToFunc(const char * name)234 CMDStringToFunc(const char *name)
235 {
236 int i;
237
238 for (i = 0; i < NumCMDFuncs; i++) {
239 if (!strcmp(name, CMDFuncs[i].name) || !strcmp(name, CMDFuncs[i].alias)) {
240 return &CMDFuncs[i];
241 }
242 }
243 return NULL;
244 }
245
246 static int
CMDSplitIntoWords(char * line,char ** argv)247 CMDSplitIntoWords(char *line, char **argv)
248 {
249 char quotechar;
250 int argc;
251
252 argc = 0;
253 while (*line) {
254 while (isspace(*line))
255 line++;
256 if (!*line)
257 break;
258 if (*line == '"') {
259 quotechar = *line++;
260 *argv++ = line;
261 argc++;
262 while (*line && *line != quotechar)
263 line++;
264 if (*line)
265 *line++ = '\0';
266 }
267 else {
268 *argv++ = line;
269 argc++;
270 while (*line && !isspace(*line))
271 line++;
272 if (*line)
273 *line++ = '\0';
274 }
275 }
276 *argv = NULL;
277 return argc;
278 }
279
280 static CMDResult
CMDHelp(int argc,char ** argv)281 CMDHelp(int argc, char **argv)
282 {
283 int i;
284 CMDFuncPtr func;
285
286 if (argc == 1) {
287 for (i = 0; i < NumCMDFuncs; i++)
288 printf("%-10s%s\n", CMDFuncs[i].name, CMDFuncs[i].usage);
289 }
290 else {
291 for (i = 1; i < argc; i++) {
292 func = CMDStringToFunc(argv[i]);
293 if (!func) {
294 printf("%-10s unknown command\n", argv[i]);
295 return CMDSyntax;
296 }
297 printf("%-10s %s\n%s", func->name, func->usage, func->help);
298 }
299 }
300 return CMDDebug;
301 }
302
303 static void
CMDSyntaxError(int argc,char ** argv)304 CMDSyntaxError(int argc, char **argv)
305 {
306 printf("Syntax error in:");
307 while (*argv)
308 printf(" %s", *argv++);
309 printf("\n");
310 }
311
312 void
ReadCommands(void)313 ReadCommands(void)
314 {
315 int argc;
316 char line[1024];
317 char *argv[20];
318 CMDResult result;
319 CMDFuncPtr func;
320 static int here;
321
322 if (here)
323 return;
324 here = 1;
325 for (;;) {
326 printf("> ");
327 if (!fgets(line, sizeof line, stdin)) {
328 if (feof(stdin)) {
329 strncpy(line, "quit", sizeof(line));
330 }
331 else {
332 printf("Error: %s\n", strerror(errno));
333 break;
334 }
335 }
336 argc = CMDSplitIntoWords(line, argv);
337 if (argc > 0) {
338 func = CMDStringToFunc(argv[0]);
339 if (!func)
340 CMDSyntaxError(argc, argv);
341 else {
342 result = (*func->func) (argc, argv);
343 switch (result) {
344 case CMDSyntax:
345 CMDSyntaxError(argc, argv);
346 break;
347 case CMDError:
348 printf("Error\n");
349 break;
350 case CMDProceed:
351 here = 0;
352 return;
353 default:
354 break;
355 }
356 }
357 }
358 }
359 printf("...\n");
360 here = 0;
361 }
362
363 int SingleStep;
364 int BreakPoint;
365 BP *breakPoints;
366 int breakPointNumber;
367
368 void
TestBreakPoints(const unsigned char * buf,long n)369 TestBreakPoints(const unsigned char *buf, long n)
370 {
371 BP *bp;
372
373 for (bp = breakPoints; bp; bp = bp->next) {
374 if (bp->enabled) {
375 if (bp->request == buf[0]) {
376 if (bp->request < EXTENSION_MIN_REQ) /* Core protocol, not extension */
377 break;
378 else if ((bp->minorop == -1) || (bp->minorop == buf[1]))
379 /* extension, either matching minor opcode or all minor opcodes */
380 break;
381 }
382 }
383 }
384 if (bp) {
385 if (bp->request < EXTENSION_MIN_REQ)
386 printf("Breakpoint on request %d\n", bp->request);
387 else if (bp->minorop == -1)
388 printf("Breakpoint on extension %d\n", bp->request);
389 else
390 printf("Breakpoint on extension %d, minor request %d\n",
391 bp->request, bp->minorop);
392 ReadCommands();
393 }
394 }
395
396 static void
setBreakPoint(void)397 setBreakPoint(void)
398 {
399 Boolean b = false;
400 BP *bp;
401 FD fd;
402
403 if (SingleStep)
404 b = true;
405 else {
406 for (bp = breakPoints; bp; bp = bp->next) {
407 if (bp->enabled) {
408 b = true;
409 break;
410 }
411 }
412 }
413 if (b != BreakPoint) {
414 BreakPoint = b;
415 for (fd = 0; fd < HighestFD; fd++) {
416 if (FDD[fd].Busy && FDD[fd].InputHandler == DataFromClient) {
417 if (BreakPoint)
418 SetBufLimit(fd);
419 else
420 ClearBufLimit(fd);
421 }
422 }
423 }
424 }
425
426 static CMDResult
CMDBreak(int argc,char ** argv)427 CMDBreak(int argc, char **argv)
428 {
429 BP *bp, **prev;
430 int request, minorop;
431 char *minorname;
432
433 if (argc == 1) {
434 for (bp = breakPoints; bp; bp = bp->next) {
435 printf("%3d: %s %3d ", bp->number,
436 bp->enabled ? "enabled" : "disabled", bp->request);
437 PrintENUMERATED(&bp->request, 1, TD[REQUEST].ValueList);
438 if (bp->minorop != -1)
439 printf(":%d", bp->minorop);
440 printf("\n");
441 }
442 }
443 else {
444 for (prev = &breakPoints; *prev; prev = &(*prev)->next);
445 while (*++argv) {
446 if ((minorname = strchr(*argv, ':'))) {
447 *minorname = '\0';
448 if (!CMDStringToInt(minorname + 1, &minorop) ||
449 (minorop < 0) || (minorop > 255)) {
450 *minorname = ':'; /* restore string for error message */
451 return CMDSyntax;
452 }
453 }
454 else {
455 minorop = -1;
456 }
457 request = GetXRequestFromName(*argv);
458 if (request < 0 && !CMDStringToInt(*argv, &request))
459 return CMDSyntax;
460 if ((request < EXTENSION_MIN_REQ) && (minorname != NULL)) {
461 *minorname = ':'; /* restore string for error message */
462 return CMDSyntax;
463 }
464 bp = malloc(sizeof(BP));
465 bp->number = ++breakPointNumber;
466 bp->request = request;
467 bp->minorop = minorop;
468 bp->enabled = true;
469 bp->next = NULL;
470 *prev = bp;
471 prev = &bp->next;
472 }
473 }
474 setBreakPoint();
475 return CMDDebug;
476 }
477
478 static CMDResult
CMDCont(int argc,char ** argv)479 CMDCont(int argc, char **argv)
480 {
481 SingleStep = 0;
482 return CMDProceed;
483 }
484
485 static CMDResult
CMDDisable(int argc,char ** argv)486 CMDDisable(int argc, char **argv)
487 {
488 BP *bp;
489 int number;
490
491 if (argc == 1) {
492 printf("Disabling all breakpoints...\n");
493 for (bp = breakPoints; bp; bp = bp->next)
494 bp->enabled = false;
495 }
496 else {
497 while (*++argv) {
498 if (!CMDStringToInt(*argv, &number))
499 return CMDSyntax;
500 for (bp = breakPoints; bp; bp = bp->next)
501 if (bp->number == number) {
502 bp->enabled = false;
503 break;
504 }
505 if (!bp) {
506 printf("No such breakpoint %s\n", *argv);
507 }
508 }
509 }
510 setBreakPoint();
511 return CMDDebug;
512 }
513
514 static CMDResult
CMDDelete(int argc,char ** argv)515 CMDDelete(int argc, char **argv)
516 {
517 BP *bp, **prev;
518 int number;
519
520 if (argc == 1) {
521 printf("Deleting all breakpoints...\n");
522 while ((bp = breakPoints) != NULL) {
523 breakPoints = bp->next;
524 free(bp);
525 }
526 }
527 else {
528 while (*++argv) {
529 if (!CMDStringToInt(*argv, &number))
530 return CMDSyntax;
531 for (prev = &breakPoints; (bp = *prev) != NULL; prev = &bp->next) {
532 if (bp->number == number) {
533 *prev = bp->next;
534 free(bp);
535 break;
536 }
537 }
538 if (!bp) {
539 printf("No such breakpoint %s\n", *argv);
540 }
541 }
542 }
543 setBreakPoint();
544 return CMDDebug;
545 }
546
547 static CMDResult
CMDEnable(int argc,char ** argv)548 CMDEnable(int argc, char **argv)
549 {
550 BP *bp;
551 int number;
552
553 if (argc == 1) {
554 printf("Enabling all breakpoints...\n");
555 for (bp = breakPoints; bp; bp = bp->next)
556 bp->enabled = true;
557 }
558 else {
559 while (*++argv) {
560 if (!CMDStringToInt(*argv, &number))
561 return CMDSyntax;
562 for (bp = breakPoints; bp; bp = bp->next)
563 if (bp->number == number) {
564 bp->enabled = true;
565 break;
566 }
567 if (!bp) {
568 printf("No such breakpoint %s\n", *argv);
569 }
570 }
571 }
572 setBreakPoint();
573 return CMDDebug;
574 }
575
576 static CMDResult
CMDStep(int argc,char ** argv)577 CMDStep(int argc, char **argv)
578 {
579 SingleStep = 1;
580 setBreakPoint();
581 return CMDProceed;
582 }
583
584 #ifdef __SUNPRO_C
585 /* prevent "Function has no return statement" error for CMDQuit */
586 #pragma does_not_return(exit)
587 #endif
588
589 static CMDResult
CMDQuit(int argc,char ** argv)590 CMDQuit(int argc, char **argv)
591 {
592 printf("exiting...\n");
593 exit(0);
594 }
595
596 static CMDResult
CMDLevel(int argc,char ** argv)597 CMDLevel(int argc, char **argv)
598 {
599 int level;
600
601 if (argc == 1)
602 printf("Level: %d\n", XVerbose);
603 else if (argc == 2 && CMDStringToInt(argv[1], &level))
604 XVerbose = level;
605 else
606 return CMDSyntax;
607 return CMDDebug;
608 }
609
610 static CMDResult
CMDAudio(int argc,char ** argv)611 CMDAudio(int argc, char **argv)
612 {
613 int level;
614
615 if (argc == 1)
616 printf("Audio Level: %d\n", NasVerbose);
617 else if (argc == 2 && CMDStringToInt(argv[1], &level))
618 NasVerbose = level;
619 else
620 return CMDSyntax;
621 return CMDDebug;
622 }
623
624 /* ********************************************** */
625 /* */
626 /* */
627 /* ********************************************** */
628
629 short
GetServerport(void)630 GetServerport(void)
631 {
632 short port;
633
634 enterprocedure("GetServerport");
635
636 port = ServerBasePort + ServerOutPort + ServerDisplay;
637 debug(4, (stderr, "Server service is on port %d\n", port));
638 return (port);
639 }
640
641 static short
GetScopePort(void)642 GetScopePort(void)
643 {
644 short port;
645
646 enterprocedure("GetScopePort");
647
648 port = ServerBasePort + ServerInPort + ServerDisplay;
649 debug(4, (stderr, "scope service is on port %d\n", port));
650 return (port);
651 }
652
653 /* ********************************************** */
654 /* */
655 /* ********************************************** */
656
657 static void _X_NORETURN
Usage(void)658 Usage(void)
659 {
660 fprintf(stderr,
661 "Usage: xscope\n"
662 " [-h<server-host>]\n"
663 " [-i<in-port>]\n"
664 " [-o<out-port>]\n"
665 " [-d<display-number>]\n"
666 " [-v<n>] -- verbose output\n"
667 " [-r] -- raw output\n"
668 " [-a<n>] -- audio verbose output\n"
669 " [-q] -- quiet output\n"
670 " [-D<debug-level>]\n"
671 " [-I] -- start in interactive mode\n"
672 " [-S<n>] -- start/stop on SIGUSR1\n"
673 " [-V] -- output version information and exit\n"
674 " [-t] -- terminate when all clients close\n");
675 exit(1);
676 }
677
678 static void
InitializeLocale(void)679 InitializeLocale(void)
680 {
681 setlocale(LC_CTYPE, "");
682
683 #ifdef HAVE_LANGINFO_H
684 {
685 const char *charmap = nl_langinfo (CODESET);
686
687 if (charmap != NULL && strcmp(charmap, "UTF-8") == 0)
688 IsUTF8locale = 1;
689 }
690 #endif
691 }
692
693 static void
ScanArgs(int argc,char ** argv)694 ScanArgs(int argc, char **argv)
695 {
696 XVerbose = 1; /* default verbose-ness level */
697 NasVerbose = 1;
698 Raw = 0;
699
700 /* Scan argument list */
701 while (--argc > 0) {
702 ++argv;
703 if (**argv == '-')
704 switch (*++*argv) {
705 /*
706 debug levels:
707 2 - trace each procedure entry
708 4 - I/O, connections
709 8 - Scope internals
710 16 - Message protocol
711 32 - 64 - was malloc, now unused
712 128 - 256 - really low level
713 */
714 case 'D':
715 debuglevel = atoi(++*argv);
716 if (debuglevel == 0)
717 debuglevel = 255;
718 XVerbose = 7;
719 debug(1, (stderr, "debuglevel = %d\n", debuglevel));
720 break;
721
722 case 'I':
723 Interrupt = 1;
724 break;
725
726 case 'S':
727 HandleSIGUSR1 = 1;
728 ScopeEnabled = atoi(++*argv);
729 break;
730
731 case 'V': /* print version & exit */
732 printf("%s\n", PACKAGE_STRING);
733 exit(0);
734
735 case 'q': /* quiet mode */
736 XVerbose = 0;
737 debug(1, (stderr, "Verbose = %d\n", XVerbose));
738 break;
739
740 case 'r': /* raw mode */
741 Raw = 1;
742 debug(1, (stderr, "Raw = %d\n", Raw));
743 break;
744
745 case 'v': /* verbose mode */
746 XVerbose = atoi(++*argv);
747 debug(1, (stderr, "Verbose = %d\n", XVerbose));
748 break;
749
750 case 'o':
751 ServerOutPort = atoi(++*argv);
752 if (ServerOutPort <= 0)
753 ServerOutPort = 0;
754 debug(1, (stderr, "ServerOutPort = %ld\n", ServerOutPort));
755 break;
756
757 case 'd':
758 ServerDisplay = atoi(++*argv);
759 if (ServerDisplay <= 0)
760 ServerDisplay = 0;
761 debug(1, (stderr, "ServerDisplay = %ld\n", ServerDisplay));
762 break;
763
764 case 'i':
765 ServerInPort = atoi(++*argv);
766 if (ServerInPort <= 0)
767 ServerInPort = 0;
768 debug(1, (stderr, "ServerInPort = %ld\n", ServerInPort));
769 break;
770
771 case 'h':
772 if (++*argv != NULL && **argv != '\0'
773 && (strlen(*argv) < sizeof(ServerHostName)))
774 strcpy(ServerHostName, *argv);
775 debug(1, (stderr, "ServerHostName = %s\n", ServerHostName));
776 break;
777
778 case 'f':
779 if (++*argv != NULL && **argv != '\0')
780 RawFile = *argv;
781 else if (argv[1] != NULL) {
782 RawFile = argv[1];
783 argv++; argc--;
784 }
785 debug(1, (stderr, "RawFile = %s\n", RawFile));
786 break;
787
788 case 'T':
789 TranslateText = 1;
790 break;
791
792 case 'A':
793 DoAudio = 1;
794 break;
795
796 case 'a': /* verbose mode */
797 NasVerbose = atoi(++*argv);
798 debug(1, (stderr, "NasVerbose = %d\n", NasVerbose));
799 break;
800
801 case 'n': /* NAS server host */
802 if (++*argv != NULL && **argv != '\0')
803 (void) strcpy(AudioServerHostName, *argv);
804 debug(1, (stderr, "AudioServerHostName = %s\n",
805 AudioServerHostName));
806 break;
807 case 't':
808 TerminateClose = 1;
809 break;
810
811 default:
812 fprintf(stderr, "Unknown option %c\n", **argv);
813 Usage();
814 break;
815
816 }
817 else {
818 /* file argument to scope -- error */
819 Usage();
820 }
821 }
822
823 /* check for different port numbers or different machines */
824 if (ServerInPort == ServerOutPort)
825 if (ServerHostName[0] == '\0') {
826 fprintf(stderr, "Can't have xscope on same port as server (%ld)\n",
827 ServerInPort);
828 Usage();
829 }
830
831 }
832
833
834 /* ********************************************** */
835 /* */
836 /* ********************************************** */
837
838 int
main(int argc,char ** argv)839 main(int argc, char **argv)
840 {
841 InitializeLocale();
842 ScanArgs(argc, argv);
843 InitializeFD();
844 InitializeX11();
845 if (DoAudio)
846 InitializeAudio();
847 SetUpStdin();
848 if (RawFile) {
849 SetUpRawFile(RawFile);
850 } else {
851 SetUpConnectionSocket(GetScopePort(), NewConnection);
852 if (DoAudio)
853 SetUpConnectionSocket(GetScopePort() + 2000, NewAudio);
854 }
855 SetSignalHandling();
856
857 return MainLoop();
858 }
859
860 void
TimerExpired(void)861 TimerExpired(void)
862 {
863 debug(16, (stderr, "Timer tick\n"));
864 }
865
866 /* ********************************************** */
867 /* */
868 /* ********************************************** */
869
870 /*
871 here is where we would add code to allow control from
872 the keyboard. We would want to read a command and
873 interpret it. Possibilties:
874
875 (a) verbose level setting
876 (b) reset time
877 (c) save X requests to a file.
878 (d) replay X requests from a file.
879 (e) allow fake events, errors to be generated.
880 */
881
882 static void
ReadStdin(FD fd)883 ReadStdin(FD fd)
884 {
885 char buf[2048];
886 long n;
887
888 enterprocedure("ReadStdin");
889 n = read(fd, buf, 2048);
890 debug(4, (stderr, "read %ld bytes from stdin\n", n));
891 }
892
893 static void
SetUpStdin(void)894 SetUpStdin(void)
895 {
896 enterprocedure("SetUpStdin");
897 UsingFD(fileno(stdin), ReadStdin, NULL, NULL);
898 }
899
900 /* ************************************************************ */
901 /* */
902 /* */
903 /* ************************************************************ */
904
905 /*
906 xscope is really meant to look at one client at a time. However,
907 it can easily handle multiple clients and servers. To do so,
908 we need to have a pair of FDs: one for the client and one for the
909 server for that client. If either goes away, so does the other.
910 We need to be able to identify the other FD of a pair, so that if
911 we get input from one, we can write it to the other.
912 */
913
914 static long clientNumber = 0;
915 struct fdinfo *FDinfo;
916
917 void
SetUpPair(FD client,FD server)918 SetUpPair(FD client, FD server)
919 {
920 if (client >= 0) {
921 clientNumber += 1;
922 FDinfo[client].Server = false;
923 FDinfo[client].pair = server;
924 FDinfo[client].ClientNumber = clientNumber;
925 if (FDinfo[client].buffer == NULL) {
926 FDinfo[client].buffer = calloc(1, BUFFER_SIZE);
927 if (FDinfo[client].buffer == NULL)
928 panic("unable to allocate client buffer");
929 }
930 FDinfo[client].bufcount = 0;
931 FDinfo[client].buflimit = -1;
932 FDinfo[client].bufdelivered = 0;
933 if (server >= 0) {
934 FDinfo[server].Server = true;
935 FDinfo[server].pair = client;
936 FDinfo[server].ClientNumber = FDinfo[client].ClientNumber;
937 if (FDinfo[server].buffer == NULL) {
938 FDinfo[server].buffer = calloc(1, BUFFER_SIZE);
939 if (FDinfo[server].buffer == NULL)
940 panic("unable to allocate server buffer");
941 }
942 FDinfo[server].bufcount = 0;
943 FDinfo[server].buflimit = -1;
944 FDinfo[server].bufdelivered = 0;
945 }
946 }
947 else if (server >= 0) {
948 close(server);
949 NotUsingFD(server);
950 }
951 }
952
953 static void
ResetPair(FD client,FD server)954 ResetPair(FD client, FD server)
955 {
956 if (client >= 0) {
957 free(FDinfo[client].buffer);
958 FDinfo[client].buffer = NULL;
959 FDinfo[client].bufcount = 0;
960 FDinfo[client].buflimit = -1;
961 FDinfo[client].bufdelivered = 0;
962 }
963 if (server >= 0) {
964 free(FDinfo[server].buffer);
965 FDinfo[server].buffer = NULL;
966 FDinfo[server].bufcount = 0;
967 FDinfo[server].buflimit = -1;
968 FDinfo[server].bufdelivered = 0;
969 }
970 }
971
972 static void
CloseConnection(FD fd)973 CloseConnection(FD fd)
974 {
975 debug(4, (stderr, "close %d and %d\n", fd, FDPair(fd)));
976 ResetPair(ClientHalf(fd), ServerHalf(fd));
977 StopClientConnection(ServerHalf(fd));
978 StopServerConnection(ClientHalf(fd));
979
980 CloseFD(fd);
981 CloseFD(FDPair(fd));
982
983 if (TerminateClose)
984 exit(0);
985 }
986
987 /* ************************************************************ */
988
989 FD
FDPair(FD fd)990 FDPair(FD fd)
991 {
992 return (FDinfo[fd].pair);
993 }
994
995 FD
ClientHalf(FD fd)996 ClientHalf(FD fd)
997 {
998 if (FDinfo[fd].Server)
999 return (FDinfo[fd].pair);
1000 return (fd);
1001 }
1002
1003 FD
ServerHalf(FD fd)1004 ServerHalf(FD fd)
1005 {
1006 if (FDinfo[fd].Server)
1007 return (fd);
1008 return (FDinfo[fd].pair);
1009 }
1010
1011 const char *
ClientName(FD fd)1012 ClientName(FD fd)
1013 {
1014 static char name[12];
1015
1016 if (clientNumber <= 1)
1017 return ("");
1018 snprintf(name, sizeof(name), " %d", FDinfo[fd].ClientNumber);
1019 return (name);
1020 }
1021
1022 int
ClientNumber(FD fd)1023 ClientNumber(FD fd)
1024 {
1025 return FDinfo[fd].ClientNumber;
1026 }
1027
1028 /* ********************************************** */
1029 /* */
1030 /* ********************************************** */
1031
1032 /*
1033 * Write as much of the queued data as the receiver will accept
1034 * Block reads from the sender until the receiver gets all of the
1035 * data
1036 */
1037 void
FlushFD(FD fd)1038 FlushFD(FD fd)
1039 {
1040 long BytesToWrite = FDinfo[fd].bufcount - FDinfo[fd].bufstart;
1041 unsigned char *p = FDinfo[fd].buffer + FDinfo[fd].bufstart;
1042 int PeerFD;
1043
1044 PeerFD = FDPair(fd);
1045 if (FDinfo[fd].buflimit >= 0) {
1046 if (FDinfo[fd].bufdelivered + BytesToWrite > FDinfo[fd].buflimit)
1047 BytesToWrite = FDinfo[fd].buflimit - FDinfo[fd].bufdelivered;
1048 }
1049 while (BytesToWrite > 0) {
1050 int n1 = write(fd, (char *) p, (int) BytesToWrite);
1051
1052 debug(4, (stderr, "write %d bytes\n", n1));
1053 if (n1 < 0) {
1054 if (errno != EWOULDBLOCK && errno != EAGAIN) {
1055 perror("Error on write");
1056 if (PeerFD >= 0)
1057 CloseConnection(PeerFD);
1058 BytesToWrite = 0;
1059 }
1060 break;
1061 }
1062 else {
1063 FDinfo[fd].bufstart += n1;
1064 FDinfo[fd].bufdelivered += n1;
1065 BytesToWrite -= n1;
1066 p += n1;
1067 }
1068 }
1069 if (FDinfo[fd].bufstart == FDinfo[fd].bufcount) {
1070 if (PeerFD >= 0) {
1071 FD_CLR(PeerFD, &BlockedReadDescriptors);
1072 }
1073 FD_CLR(fd, &WriteDescriptors);
1074 FDinfo[fd].bufcount = FDinfo[fd].bufstart = 0;
1075 }
1076 else {
1077 if (PeerFD >= 0) {
1078 FD_SET(PeerFD, &BlockedReadDescriptors);
1079 }
1080 if (FDinfo[fd].buflimit != FDinfo[fd].bufdelivered) {
1081 FD_SET(fd, &WriteDescriptors);
1082 }
1083 }
1084 }
1085
1086 /* when we get data from a client, we read it in, copy it to the
1087 server for this client, then dump it to the client. Note, we don't
1088 have to have a server, if there isn't one. */
1089
1090 static void
DataFromClient(FD fd)1091 DataFromClient(FD fd)
1092 {
1093 long n;
1094 FD ServerFD;
1095
1096 Verbose = XVerbose;
1097 enterprocedure("DataFromClient");
1098 ServerFD = FDPair(fd);
1099 if (ServerFD < 0) {
1100 ServerFD = ConnectToServer(false);
1101 if (ServerFD < 0) {
1102 CloseConnection(fd);
1103 return;
1104 }
1105 SetUpPair(fd, ServerFD);
1106 }
1107
1108 n = read(fd, FDinfo[ServerFD].buffer, BUFFER_SIZE);
1109 debug(4, (stderr, "read %ld bytes from Client%s\n", n, ClientName(fd)));
1110 if (n < 0) {
1111 PrintTime();
1112 perror("Client --> read error:");
1113 CloseConnection(fd);
1114 return;
1115 }
1116 if (n == 0) {
1117 PrintTime();
1118 if (Verbose >= 0)
1119 fprintf(stdout, "Client%s --> EOF\n", ClientName(fd));
1120 CloseConnection(fd);
1121 return;
1122 }
1123
1124 FDinfo[ServerFD].bufcount = n;
1125 FDinfo[ServerFD].bufstart = 0;
1126
1127 FlushFD(ServerFD);
1128 /* also report the bytes to standard out */
1129 ReportFromClient(fd, FDinfo[ServerFD].buffer, n);
1130 }
1131
1132 /* ********************************************** */
1133 /* */
1134 /* ********************************************** */
1135
1136 /* similar situation for the server, but note that if there is no client,
1137 we close the connection down -- don't need a server with no client. */
1138
1139 static void
DataFromServer(FD fd)1140 DataFromServer(FD fd)
1141 {
1142 long n;
1143 FD ClientFD;
1144
1145 Verbose = XVerbose;
1146 ClientFD = FDPair(fd);
1147 if (ClientFD < 0) {
1148 CloseConnection(fd);
1149 return;
1150 }
1151
1152 enterprocedure("DataFromServer");
1153 n = read(fd, (char *) FDinfo[ClientFD].buffer, BUFFER_SIZE);
1154 debug(4, (stderr, "read %ld bytes from Server%s\n", n, ClientName(fd)));
1155 if (n < 0) {
1156 PrintTime();
1157 perror("read error <- Server");
1158 CloseConnection(fd);
1159 return;
1160 }
1161 if (n == 0) {
1162 PrintTime();
1163 if (Verbose >= 0)
1164 fprintf(stdout, "EOF <-- Server%s\n", ClientName(fd));
1165 CloseConnection(fd);
1166 return;
1167 }
1168
1169 FDinfo[ClientFD].bufcount = n;
1170 FDinfo[ClientFD].bufstart = 0;
1171 FlushFD(ClientFD);
1172
1173 /* also report the bytes to standard out */
1174 ReportFromServer(fd, FDinfo[ClientFD].buffer, n);
1175 }
1176
1177
1178 /* ************************************************************ */
1179 /* */
1180 /* Create New Connection to a client program and to Server */
1181 /* */
1182 /* ************************************************************ */
1183
1184 void
NewConnection(FD fd)1185 NewConnection(FD fd)
1186 {
1187 FD ClientFD = ConnectToClient(fd);
1188 FD ServerFD = ConnectToServer(true);
1189
1190 SetUpPair(ClientFD, ServerFD);
1191 }
1192
1193
1194 /* ************************************************************ */
1195
1196 static FD
ConnectToClient(FD ConnectionSocket)1197 ConnectToClient(FD ConnectionSocket)
1198 {
1199 FD ClientFD;
1200 XtransConnInfo trans_conn = NULL;
1201
1202 #ifdef USE_XTRANS
1203 XtransConnInfo listen_conn;
1204 int status;
1205 #else
1206 struct sockaddr_in from;
1207 int len = sizeof(from);
1208 int ON = 1; /* used in ioctl */
1209 #endif
1210
1211 enterprocedure("ConnectToClient");
1212
1213 #ifdef USE_XTRANS
1214 listen_conn = GetXTransConnInfo(ConnectionSocket);
1215 if ((trans_conn = _X11TransAccept(listen_conn, &status)) == NULL) {
1216 debug(4, (stderr, "Failed to accept connection\n"));
1217 return -1;
1218 }
1219 _X11TransSetOption(trans_conn, TRANS_NONBLOCKING, 1);
1220 ClientFD = _X11TransGetConnectionNumber(trans_conn);
1221 #else
1222 ClientFD = AcceptConnection(ConnectionSocket);
1223 #endif
1224 debug(4, (stderr, "Connect To Client: FD %d\n", ClientFD));
1225 if (ClientFD < 0 && errno == EWOULDBLOCK) {
1226 debug(4, (stderr, "Almost blocked accepting FD %d\n", ClientFD));
1227 panic("Can't connect to Client");
1228 }
1229 if (ClientFD < 0) {
1230 debug(4, (stderr, "NewConnection: error %d\n", errno));
1231 panic("Can't connect to Client");
1232 }
1233
1234 UsingFD(ClientFD, DataFromClient, FlushFD, trans_conn);
1235 #ifndef USE_XTRANS
1236 ioctl(ClientFD, FIOCLEX, 0);
1237 ioctl(ClientFD, FIONBIO, &ON);
1238 #endif
1239 StartClientConnection(ClientFD);
1240 return (ClientFD);
1241 }
1242
1243 /* ************************************************************ */
1244 /* */
1245 /* */
1246 /* ************************************************************ */
1247
1248
1249 FD
ConnectToServer(Boolean report)1250 ConnectToServer(Boolean report)
1251 {
1252 FD ServerFD;
1253 XtransConnInfo trans_conn = NULL; /* transport connection object */
1254 short port;
1255
1256 enterprocedure("ConnectToServer");
1257
1258 port = GetServerport();
1259
1260 if (port == ScopePort &&
1261 ((ServerHostName[0] == '\0') || strcmp(ServerHostName, ScopeHost) == 0))
1262 {
1263 char error_message[100];
1264
1265 snprintf(error_message, sizeof(error_message),
1266 "Trying to attach to myself: %s,%d\n", ServerHostName, port);
1267 panic(error_message);
1268 }
1269
1270 ServerFD = MakeConnection(ServerHostName, port, report, &trans_conn);
1271
1272 debug(4, (stderr, "Connect To Server: FD %d\n", ServerFD));
1273 if (ServerFD >= 0) {
1274 UsingFD(ServerFD, DataFromServer, FlushFD, trans_conn);
1275 StartServerConnection(ServerFD);
1276 }
1277 return (ServerFD);
1278 }
1279
1280
1281 /* ************************************************************ */
1282 /* */
1283 /* Read & decode data from a pre-recorded raw file (-r -v0) */
1284 /* */
1285 /* ************************************************************ */
1286
1287 static FILE *raw;
1288 static FD *fdMap; /* map of fds from file to current runtime */
1289 static int fdMapSize;
1290
1291 static void
SetUpRawClient(FD * returnClientFD,FD * returnServerFD)1292 SetUpRawClient(FD *returnClientFD, FD *returnServerFD)
1293 {
1294 FD ClientFD, ServerFD;
1295
1296 /* need to fake a pair of fd's */
1297 ClientFD = open("/dev/null", O_RDONLY);
1298 if (ClientFD < 1) {
1299 perror("/dev/null");
1300 panic("Can't open /dev/null for reading");
1301 }
1302 ServerFD = dup(ClientFD);
1303 if (ServerFD < 1) {
1304 perror("ClientFD");
1305 panic("Can't dup ClientFD for reading");
1306 }
1307 UsingFD(ClientFD, NULL, NULL, NULL);
1308 UsingFD(ServerFD, NULL, NULL, NULL);
1309 SetUpPair(ClientFD, ServerFD);
1310 StartClientConnection(ClientFD);
1311 StartServerConnection(ServerFD);
1312 *returnClientFD = ClientFD;
1313 *returnServerFD = ServerFD;
1314 }
1315
1316 static inline FD
GetRawFDMap(FD origFD)1317 GetRawFDMap(FD origFD)
1318 {
1319 if (origFD >= fdMapSize)
1320 return -1;
1321 return fdMap[origFD];
1322 }
1323
1324 static void
SetRawFDMap(FD origFD,FD currentFD)1325 SetRawFDMap(FD origFD, FD currentFD)
1326 {
1327 if (origFD >= fdMapSize) {
1328 int newSize = fdMapSize + 32;
1329 if (origFD >= newSize)
1330 newSize = origFD;
1331 fdMap = realloc(fdMap, newSize);
1332 if (fdMap == NULL)
1333 panic("Can't allocate memory for fdMap");
1334 memset(fdMap + fdMapSize, -1, newSize - fdMapSize);
1335 fdMapSize = newSize;
1336 }
1337 fdMap[origFD] = currentFD;
1338 debug(16, (stderr, "FD Map %d => %d\n", origFD, currentFD));
1339 }
1340
1341 static void
UnmapRawFD(FD currentFD)1342 UnmapRawFD(FD currentFD)
1343 {
1344 FD fd;
1345
1346 for (fd = 0; fd < fdMapSize; fd++) {
1347 if (fdMap[fd] == currentFD) {
1348 debug(16, (stderr, "Clearing FD Map %d => %d\n", fd, currentFD));
1349 fdMap[fd] = -1;
1350 return;
1351 }
1352 }
1353 }
1354
1355
1356 static void
DataFromRawFile(FD rawfd)1357 DataFromRawFile(FD rawfd)
1358 {
1359 Boolean fromClient = true;
1360 FD fd = -1;
1361 char *in, *n;
1362 unsigned char *out;
1363 static FD newServerFD;
1364
1365 enterprocedure("DataFromRawFile");
1366
1367 Verbose = XVerbose;
1368
1369 for (;;) {
1370
1371 /* If we already read a line last time, process it first,
1372 else get the next line for processing */
1373 if (FDinfo[rawfd].bufcount == 0) {
1374 if (fgets(FDinfo[rawfd].buffer, BUFFER_SIZE, raw) == NULL) {
1375 CloseConnection(rawfd);
1376 exit(0);
1377 }
1378 FDinfo[rawfd].bufcount = strlen(FDinfo[rawfd].buffer);
1379 debug(16, (stderr, "raw input = %s", FDinfo[rawfd].buffer));
1380 }
1381
1382 in = FDinfo[rawfd].buffer;
1383
1384 /* lines starting with space indicate change of sender */
1385 if (isspace(*in)) {
1386 /* If we already have data in buffer, process it */
1387 if ((fd != -1) && (FDinfo[fd].bufcount > 0)) {
1388 debug(16, (stderr, "reporting %d bytes from: %s %s\n",
1389 FDinfo[fd].bufcount,
1390 fromClient ? "client" : "server",
1391 ClientName(fd)));
1392 if (fromClient)
1393 ReportFromClient(fd, FDinfo[fd].buffer,
1394 FDinfo[fd].bufcount);
1395 else
1396 ReportFromServer(fd, FDinfo[fd].buffer,
1397 FDinfo[fd].bufcount);
1398 FDinfo[fd].bufcount = 0;
1399 return;
1400 }
1401 else {
1402 /* Need to parse header to figure out which direction */
1403 while (isspace(*in)) {
1404 in++;
1405 }
1406 if (strncmp(in, "Client Connect (fd ", 19) == 0) {
1407 /* New client connection */
1408 FD ClientFD, ServerFD, origFD;
1409
1410 origFD = strtol(in + 19, NULL, 10);
1411
1412 SetUpRawClient(&ClientFD, &ServerFD);
1413 SetRawFDMap(origFD, ClientFD);
1414 fd = ClientFD;
1415 fromClient = true;
1416 newServerFD = ServerFD;
1417 }
1418 else if (strncmp(in, "Server Connect (fd ", 19) == 0) {
1419 /* New client connection */
1420 FD origFD;
1421
1422 if (newServerFD == 0)
1423 panic("Server connection without matching client");
1424
1425 origFD = strtol(in + 19, NULL, 10);
1426 SetRawFDMap(origFD, newServerFD);
1427 fd = newServerFD;
1428 fromClient = false;
1429 newServerFD = 0;
1430 }
1431 else if (strstr(in, " --> EOF")) {
1432 /* Closing client connection */
1433 n = strstr(in, ": Client");
1434 if (n != NULL) {
1435 int clientid;
1436
1437 in = n + 8;
1438 if (isdigit(*in))
1439 clientid = strtol(in, NULL, 10);
1440 else
1441 clientid = 1;
1442
1443 for (fd = 0; fd < HighestFD; fd++) {
1444 if (FDinfo[fd].ClientNumber == clientid) {
1445 if (Verbose >= 0) {
1446 PrintTime();
1447 fprintf(stdout, "Client%s --> EOF\n",
1448 ClientName(fd));
1449 }
1450 UnmapRawFD(fd);
1451 UnmapRawFD(FDPair(fd));
1452 CloseConnection(fd);
1453 break;
1454 }
1455 }
1456 }
1457 fd = -1;
1458 }
1459 else {
1460 FD origFD;
1461
1462 if ((strncmp(in, "Request ", 8) == 0))
1463 fromClient = true;
1464 else
1465 fromClient = false;
1466
1467 in = strstr(in, "(fd ");
1468 if (in == NULL) {
1469 warn("Did not find fd string in input entry");
1470 warn(FDinfo[rawfd].buffer);
1471 FDinfo[rawfd].bufcount = 0;
1472 continue;
1473 }
1474
1475 origFD = strtol(in + 4, NULL, 10);
1476 fd = GetRawFDMap(origFD);
1477 if (fd == -1) {
1478 debug(16, (stderr, "origFD = %d\n", origFD));
1479 warn("Unknown fd in input entry");
1480 FDinfo[rawfd].bufcount = 0;
1481 continue;
1482 }
1483 }
1484 debug(16, (stderr, "raw data from: %s\n",
1485 fromClient ? "client" : "server"));
1486 /* skip over rest of header */
1487 n = strchr(in, ':');
1488 if (n != NULL)
1489 in = n + 1;
1490 while (isspace(*in))
1491 in++;
1492 }
1493 }
1494
1495 if (fd == -1) {
1496 /* dump data we don't know what to do with */
1497 FDinfo[rawfd].bufcount = 0;
1498 continue;
1499 }
1500
1501 out = FDinfo[fd].buffer + FDinfo[fd].bufcount;
1502
1503 while (*in != '\0') {
1504 unsigned char val;
1505
1506 if ((in[0] >= '0') && in[0] <= '9') {
1507 val = (in[0] - '0') << 4;
1508 }
1509 else if ((in[0] >= 'a') && in[0] <= 'f') {
1510 val = (in[0] - 'a' + 0x0a) << 4;
1511 }
1512 else {
1513 warn(in);
1514 warn("invalid raw file input");
1515 break;
1516 }
1517
1518 if ((in[1] >= '0') && in[1] <= '9') {
1519 val += (in[1] - '0');
1520 }
1521 else if ((in[1] >= 'a') && in[1] <= 'f') {
1522 val += (in[1] - 'a' + 0x0a);
1523 }
1524 else {
1525 warn(in + 1);
1526 warn("invalid raw file input");
1527 break;
1528 }
1529
1530 if (in[2] == ' ') {
1531 in += 3;
1532 } else {
1533 in += 2;
1534 }
1535 while ((*in == '\r') || (*in == '\n'))
1536 in++;
1537
1538 *out++ = val;
1539 FDinfo[fd].bufcount++;
1540
1541 /* If buffer is full, process what we have now */
1542 if (FDinfo[fd].bufcount >= BUFFER_SIZE) {
1543 if (fromClient)
1544 ReportFromClient(fd, FDinfo[fd].buffer,
1545 FDinfo[fd].bufcount);
1546 else
1547 ReportFromServer(fd, FDinfo[fd].buffer,
1548 FDinfo[fd].bufcount);
1549 FDinfo[fd].bufcount = 0;
1550 out = FDinfo[fd].buffer;
1551 }
1552 }
1553 FDinfo[rawfd].bufcount = 0;
1554 }
1555 }
1556
1557 static void
SetUpRawFile(const char * filename)1558 SetUpRawFile(const char *filename)
1559 {
1560 FD fd;
1561 enterprocedure("SetUpRawFile");
1562
1563 if (strcmp(filename, "-") == 0)
1564 raw = stdin;
1565 else
1566 raw = fopen(filename, "r");
1567 if (raw == NULL) {
1568 perror(filename);
1569 panic("Can't open raw file for reading");
1570 }
1571 fd = fileno(raw);
1572 debug(4, (stderr, "Opened raw file %s: FD %d\n", filename, fd));
1573
1574 if (FDinfo[fd].buffer == NULL) {
1575 FDinfo[fd].buffer = calloc(1, BUFFER_SIZE);
1576 if (FDinfo[fd].buffer == NULL)
1577 panic("unable to allocate client buffer");
1578 }
1579 FDinfo[fd].bufcount = 0;
1580 FDinfo[fd].buflimit = -1;
1581 FDinfo[fd].bufdelivered = 0;
1582
1583 UsingFD(fd, DataFromRawFile, NULL, NULL);
1584 }
1585