master
c 1,414 lines 31.6 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 dev_pt_common.c
8
9 Abstract:
10
11 This file is a test for the Pseudo Terminals: /dev/ptmx, /dev/pts/<n>
12 devices.
13
14 --*/
15
16 #include "dev_pt_common.h"
17
18 pid_t ForkPtyCommon(int* PtmFdOut, int* PtsFdOut, bool UseMasterEndpoint);
19
20 void DumpBuffer(const char Data[], size_t DataSize)
21
22 /*++
23
24 Routine Description:
25
26 This routine will log the Data.
27
28 Arguments:
29
30 Data - Supplies the buffer to be filled.
31
32 DataSize - Supplies the size of the data buffer.
33
34 Return Value:
35
36 None
37
38 --*/
39
40 {
41
42 size_t Index;
43 for (Index = 0; Index < DataSize; Index++)
44 {
45 printf("%d:(", Data[Index]);
46 if (Data[Index] == '\n')
47 {
48 printf("\\n");
49 }
50 else if (Data[Index] == '\r')
51 {
52 printf("\\r");
53 }
54 else if (Data[Index] == '\t')
55 {
56 printf("\\t");
57 }
58 else
59 {
60 printf("%c", Data[Index]);
61 }
62
63 printf(") ");
64 }
65
66 return;
67 }
68
69 int GetPtSerialNumFromDeviceString(const char PtsNameString[])
70
71 /*++
72
73 Routine Description:
74
75 This routine will parse the PTS (Pseudo Terminal Slave) device name and
76 retrieve the Serial Number from the string.
77
78 Arguments:
79
80 PtsNameString - Supplies the device name of the PTS. The name should be
81 of the format "/dev/pts/<n>" where 'n' is the Serial Number. The string
82 should also be NULL terminated.
83
84 Return Value:
85
86 Returns the Serial Number, which is >=0 on success, -1 on failure.
87
88 --*/
89
90 {
91
92 int NumberOfItemsScanned;
93 int Result;
94 int SerialNumber;
95
96 SerialNumber = 0;
97
98 NumberOfItemsScanned = sscanf(PtsNameString, "/dev/pts/%d", &SerialNumber);
99 if (NumberOfItemsScanned != 1)
100 {
101 Result = -1;
102 goto ErrorExit;
103 }
104
105 Result = SerialNumber;
106
107 ErrorExit:
108 return Result;
109 }
110
111 int GetRandomMessage(char Message[], size_t MessageSize, bool CompleteMessage)
112 /*++
113
114 Routine Description:
115
116 This routine will fill the message buffer with random bytes for the
117 specified size. If a complete message is requested, then it will set the
118 last byte in the message with the completion character.
119
120 Arguments:
121
122 Message - Supplies the buffer to be filled with data.
123
124 MessageSize - Supplies the size to be filled. Size should be >=1.
125
126 CompleteMessage - Supplies the flag which indicates whether the message
127 should be completed with a terminating character or not.
128
129 Return Value:
130
131 Returns 0 on success, -1 on failure.
132
133 --*/
134
135 {
136 size_t Itr;
137 size_t NumBytesToFill;
138
139 //
140 // If the message has to be completed, last byte is reserved for the
141 // terminating character.
142 //
143
144 if (CompleteMessage != FALSE)
145 {
146 NumBytesToFill = MessageSize - 1;
147 Message[NumBytesToFill] = '\n';
148 }
149 else
150 {
151 NumBytesToFill = MessageSize;
152 }
153
154 for (Itr = 0; Itr < NumBytesToFill; Itr += 1)
155 {
156
157 //
158 // TODO_LX_PTYT: Randomize the data.
159 //
160
161 Message[Itr] = 'A' + (Itr % 26);
162 }
163
164 return 0;
165 }
166
167 int OpenMasterSubordinate(int* PtmFd, int* PtsFd, char* PtsDevName, int* SerialNumber)
168
169 /*++
170
171 Routine Description:
172
173 This routine will open a master and subordinate pseudo terminal.
174 It will also extract the serial number of the subordinate and
175 return it in 'SerialNumber'.
176
177 Arguments:
178
179 PtmFd - Supplies the pointer which will be set to the FD of the
180 master, on success. This parameter is not optional.
181
182 PtsFd - Supplies the pointer which will be set to the FD of the
183 subordinate, on success. This parameter is not optional.
184
185 PtsDevName - Supplies the pointer to the buffer that will hold
186 the subordinate device name, on success. This parameter is
187 optional.
188
189 SerialNumber - Supplies the pointer which will be set to the
190 serial number of the subordinate pseudo terminal. This
191 parameter is optional.
192
193 Return Value:
194
195 0 on success, error code on failure.
196 --*/
197
198 {
199
200 int Fdm;
201 int Fds;
202 char LocalBuffer[PTS_DEV_NAME_BUFFER_SIZE];
203 int Result;
204 int SubordinateSerialNumber;
205
206 //
207 // Initialize locals
208 //
209
210 Result = -1;
211 Fdm = -1;
212 Fds = -1;
213
214 LxtCheckErrno((Fdm = open("/dev/ptmx", O_RDWR)));
215 LxtCheckErrno(grantpt(Fdm));
216 LxtCheckErrno(unlockpt(Fdm));
217 LxtCheckErrno(ptsname_r(Fdm, LocalBuffer, PTS_DEV_NAME_BUFFER_SIZE));
218 LxtCheckErrno(Fds = open(LocalBuffer, O_RDWR));
219 if (PtsDevName != NULL)
220 {
221 strcpy(PtsDevName, LocalBuffer);
222 }
223
224 if (SerialNumber != NULL)
225 {
226 LxtCheckErrno(SubordinateSerialNumber = GetPtSerialNumFromDeviceString(LocalBuffer));
227 *SerialNumber = SubordinateSerialNumber;
228 }
229
230 *PtmFd = Fdm;
231 *PtsFd = Fds;
232 Fdm = -1;
233 Fds = -1;
234 Result = 0;
235
236 ErrorExit:
237 if (Fdm != -1)
238 {
239 close(Fdm);
240 }
241
242 if (Fds != -1)
243 {
244 close(Fds);
245 }
246
247 return Result;
248 }
249
250 pid_t ForkPty(int* PtmFdOut, int* PtsFdOut)
251
252 /*++
253
254 Routine Description:
255
256 This routine sets up a new process as a foreground process using PtsFd as
257 its controlling terminal.
258
259 Arguments:
260
261 PtmFdOut - Supplies a pointer to receive the master file descriptor.
262
263 PtsFdOut - Supplies a pointer to receive the subordinate file descriptor.
264
265 Return Value:
266
267 Returns pid of newly forked process to the parent, zero to the child and
268 <0 on error.
269
270 --*/
271
272 {
273
274 return ForkPtyCommon(PtmFdOut, PtsFdOut, false);
275 }
276
277 int ForkPtyBackground(int* PtmFdOut, int* PtsFdOut, pid_t* ForegroundIdOut)
278
279 /*++
280
281 Routine Description:
282
283 This routine sets up a new process as a background process using PtsFd as
284 its controlling terminal.
285
286 Arguments:
287
288 PtmFdOut - Supplies a pointer to receive the master file descriptor.
289
290 PtsFdOut - Supplies a pointer to receive the subordinate file descriptor.
291
292 ForegroundId - Supplies a pointer to receive the ID of the foreground
293 process.
294
295 Return Value:
296
297 Returns pid of newly forked process to the parent, zero to the child and
298 <0 on error.
299
300 --*/
301
302 {
303
304 int ChildPid;
305 int GrandChildPid;
306 int GrandChildStatus;
307 int PtmFd;
308 int PtsFd;
309 int Result;
310
311 ChildPid = -1;
312 GrandChildPid = -1;
313 PtmFd = -1;
314 PtsFd = -1;
315
316 LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));
317 if (ChildPid == 0)
318 {
319 *ForegroundIdOut = getpid();
320 LxtCheckErrno(GrandChildPid = fork());
321 if (GrandChildPid == 0)
322 {
323 LxtCheckErrno(setpgid(0, 0));
324 }
325 else
326 {
327 LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(GrandChildPid, &GrandChildStatus, 0)));
328 LxtCheckResult(WIFEXITED(GrandChildStatus) ? 0 : -1);
329 LxtCheckResult((int)(char)WEXITSTATUS(GrandChildStatus));
330 }
331 }
332 else
333 {
334 *ForegroundIdOut = ChildPid;
335 }
336
337 *PtmFdOut = PtmFd;
338 PtmFd = -1;
339 *PtsFdOut = PtsFd;
340 PtsFd = -1;
341
342 ErrorExit:
343 if (PtmFd != -1)
344 {
345 close(PtmFd);
346 }
347
348 if (PtsFd != -1)
349 {
350 close(PtsFd);
351 }
352
353 if ((ChildPid == 0) && (GrandChildPid > 0))
354 {
355 exit(Result);
356 }
357
358 return ChildPid;
359 }
360
361 pid_t ForkPtyCommon(int* PtmFdOut, int* PtsFdOut, bool UseMasterEndpoint)
362
363 /*++
364
365 Routine Description:
366
367 This routine sets up a new process as a foreground process using PtsFd as
368 its controlling terminal.
369
370 Arguments:
371
372 PtmFdOut - Supplies a pointer to receive the master file descriptor.
373
374 PtsFdOut - Supplies a pointer to receive the subordinate file descriptor.
375
376 UseMasterEndpoint - Supplies a flag indicating whether the master or
377 subordinate endpoint should be set as the controlling terminal.
378
379 Return Value:
380
381 Returns pid of newly forked process to the parent, zero to the child and
382 <0 on error.
383
384 --*/
385
386 {
387
388 pid_t ChildPid;
389 int PtmFd;
390 int PtsFd;
391 int Result;
392 int SerialNumber;
393 pid_t SessionId;
394
395 //
396 // Initialize locals
397 //
398
399 ChildPid = -1;
400 PtmFd = -1;
401 PtsFd = -1;
402
403 //
404 // Open Master-Subordinate
405 //
406
407 LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));
408 LxtLogInfo("Master opened at FD:%d", PtmFd);
409 LxtLogInfo("Subordinate Serial Number: %d", SerialNumber);
410 LxtLogInfo("Subordinate opened at FD:%d", PtsFd);
411
412 LxtCheckErrno(ChildPid = fork());
413 if (ChildPid == 0)
414 {
415
416 //
417 // Move to a new session
418 //
419
420 LxtCheckErrno(SessionId = setsid());
421
422 //
423 // Set the fd as the controlling terminal for the session, calling
424 // again should not fail.
425 //
426
427 LxtCheckErrno(ioctl((UseMasterEndpoint) ? PtmFd : PtsFd, TIOCSCTTY, (char*)NULL));
428 LxtCheckErrno(ioctl((UseMasterEndpoint) ? PtmFd : PtsFd, TIOCSCTTY, (char*)NULL));
429 }
430
431 *PtmFdOut = PtmFd;
432 PtmFd = -1;
433 *PtsFdOut = PtsFd;
434 PtsFd = -1;
435
436 ErrorExit:
437 if (PtmFd != -1)
438 {
439 close(PtmFd);
440 }
441
442 if (PtsFd != -1)
443 {
444 close(PtsFd);
445 }
446
447 return ChildPid;
448 }
449
450 pid_t ForkPtyMaster(int* PtmFdOut, int* PtsFdOut)
451
452 /*++
453
454 Routine Description:
455
456 This routine sets up a new process as a foreground process using PtmFd as
457 its controlling terminal.
458
459 Arguments:
460
461 PtmFdOut - Supplies a pointer to receive the master file descriptor.
462
463 PtsFdOut - Supplies a pointer to receive the subordinate file descriptor.
464
465 Return Value:
466
467 Returns pid of newly forked process to the parent, zero to the child and
468 <0 on error.
469
470 --*/
471
472 {
473
474 return ForkPtyCommon(PtmFdOut, PtsFdOut, true);
475 }
476
477 int RawInit(int Fd)
478
479 /*++
480
481 Routine Description:
482
483 This routine will use termios to set the FD for raw input/output.
484
485 Arguments:
486
487 Fd - Supplies the FD.
488
489 Return Value:
490
491 0 on success, error code on failure.
492
493 --*/
494
495 {
496
497 cc_t ControlArray[NCCS];
498 int Result;
499
500 //
501 // After the switch to RAW mode want no timeout and a minimum of 1 char.
502 //
503
504 Result = TerminalSettingsGetControlArray(Fd, ControlArray);
505 if (Result < 0)
506 {
507 goto ErrorExit;
508 }
509
510 ControlArray[VTIME] = 0;
511 ControlArray[VMIN] = 1;
512 Result = TerminalSettingsSetControlArray(Fd, ControlArray);
513 if (Result < 0)
514 {
515 goto ErrorExit;
516 }
517
518 //
519 // Disable echo, cannon and other flags. Set TOSTOP so signals are
520 // generated by default.
521 //
522
523 Result = TerminalSettingsSetLocalFlags(Fd, TOSTOP);
524
525 ErrorExit:
526 return Result;
527 }
528
529 int SimpleReadWriteCheck(int PtmFd, int PtsFd)
530
531 /*++
532
533 Routine Description:
534
535 This routine performs a simple read/write check on the master-subordinate
536 pseudo terminal pair. The check is as follows:
537 - Write to the master.
538 - Read from the subordinate. Read data should match the data written
539 by master.
540 - Write to the subordinate.
541 - Read data from the master. Data read from the mater should match
542 what was written by the subordinate.
543
544 Arguments:
545
546 PtmFd - Supplies the FD for the master.
547
548 PtsFd - Supplies the FD for the subordinate.
549
550 Return Value:
551
552 0 on success, error code on failure.
553
554 --*/
555
556 {
557
558 return SimpleReadWriteCheckEx(PtmFd, PtsFd, SimpleReadWriteForeground);
559 }
560
561 int SimpleReadWriteCheckEx(int PtmFd, int PtsFd, SIMPLE_READ_WRITE_MODE Mode)
562
563 /*++
564
565 Routine Description:
566
567 This routine performs a simple read/write check on the master-subordinate
568 pseudo terminal pair. The check is as follows:
569 - Write to the master.
570 - Read from the subordinate. Read data should match the data written
571 by master.
572 - Write to the subordinate.
573 - Read data from the master. Data read from the mater should match
574 what was written by the subordinate.
575
576 Arguments:
577
578 PtmFd - Supplies the FD for the master.
579
580 PtsFd - Supplies the FD for the subordinate.
581
582 Mode - Supplies a value indicating whether this access is from foreground
583 or background, and whether signals are enabled.
584
585 Return Value:
586
587 0 on success, error code on failure.
588
589 --*/
590
591 {
592
593 int BytesReadWrite;
594 int ExpectedResult;
595 const char* Greetings = "Hello there!!\n";
596 size_t GreetingsLength;
597 int PacketMode;
598 int PtmFlags;
599 char ReadBuffer[1024];
600 char* ReadBufferMaster;
601 const char* Reply = "Hi, how are you?\r";
602 size_t ReplyLength;
603 int Result;
604 struct termios TiosMaster;
605 struct termios TiosSubordinate;
606
607 LxtCheckErrno(PtmFlags = fcntl(PtmFd, F_GETFL, 0));
608
609 memset(&TiosMaster, 0, sizeof(TiosMaster));
610 LxtCheckErrno(tcgetattr(PtmFd, &TiosMaster));
611 memset(&TiosSubordinate, 0, sizeof(TiosSubordinate));
612 LxtCheckErrno(tcgetattr(PtsFd, &TiosSubordinate));
613 LxtCheckMemoryEqual(&TiosMaster, &TiosSubordinate, sizeof(TiosMaster));
614 GreetingsLength = strlen(Greetings);
615 ReplyLength = strlen(Reply);
616 if (TiosSubordinate.c_lflag & ICANON)
617 {
618 LxtLogInfo("Canonical mode.");
619 }
620 else
621 {
622 LxtLogInfo("Raw mode.");
623 --GreetingsLength;
624 --ReplyLength;
625 }
626
627 LxtCheckErrno(ioctl(PtmFd, TIOCGPKT, &PacketMode));
628 if (PacketMode != 0)
629 {
630 LxtLogInfo("Packet mode enabled.");
631 ReadBufferMaster = &ReadBuffer[1];
632 }
633 else
634 {
635 ReadBufferMaster = &ReadBuffer[0];
636 }
637
638 //
639 // Write the greetings message to the master.
640 //
641
642 LxtLogInfo("Writing to master");
643 ExpectedResult = GreetingsLength;
644 LxtCheckErrno(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult));
645 LxtCheckFnResults("write", BytesReadWrite, ExpectedResult);
646 LxtLogInfo("Master(FD:%d) --> subordinate(FD:%d):%*s", PtmFd, PtsFd, GreetingsLength, Greetings);
647
648 //
649 // Canonical mode should echo the input back to the master with a
650 // carriage-return and newline.
651 //
652
653 if (TiosSubordinate.c_lflag & ICANON)
654 {
655 LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));
656 if (PacketMode != 0)
657 {
658 LxtCheckEqual(ReadBuffer[0], 0, "%hhd");
659 if (BytesReadWrite > 0)
660 {
661 BytesReadWrite -= 1;
662 }
663 }
664
665 ReadBufferMaster[BytesReadWrite] = '\0';
666 LxtLogInfo("Echo received by master(FD:%d):%s", PtmFd, ReadBufferMaster);
667 LxtLogInfo("Last character = %d [\\n = %d, \\r = %d]", ReadBufferMaster[BytesReadWrite - 1], '\n', '\r');
668 LxtCheckFnResults("read", BytesReadWrite, (ExpectedResult + 1));
669 if ((ReadBufferMaster[BytesReadWrite - 1] != '\n') || (ReadBufferMaster[BytesReadWrite - 2] != '\r'))
670 {
671 LxtLogError("Echo to master(FD:%d) does not end with \r\n.", PtmFd);
672
673 Result = -1;
674 goto ErrorExit;
675 }
676
677 ReadBufferMaster[BytesReadWrite - 2] = '\n';
678 if (memcmp(ReadBufferMaster, Greetings, ExpectedResult) != 0)
679 {
680 LxtLogError(
681 "Echo to master(FD:%d) does not match what was "
682 "written.",
683 PtmFd);
684
685 Result = -1;
686 goto ErrorExit;
687 }
688 }
689
690 //
691 // Read from subordinate.
692 //
693
694 memset(ReadBuffer, 0, sizeof(ReadBuffer));
695 LxtLogInfo("Reading from subordinate");
696 if (Mode == SimpleReadWriteForeground)
697 {
698 LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));
699 LxtLogInfo("Message received by subordinate(FD:%d):%s", PtsFd, ReadBuffer);
700 LxtLogInfo("Last character = %d [\\n = %d, \\r = %d]", ReadBuffer[BytesReadWrite - 1], '\n', '\r');
701 LxtCheckFnResults("read", BytesReadWrite, ExpectedResult);
702
703 //
704 // Compare the messages.
705 //
706
707 if (memcmp(ReadBuffer, Greetings, BytesReadWrite) != 0)
708 {
709 LxtLogError(
710 "Data read from subordinate(FD:%d) does not match what was "
711 "written by master(FD:%d).",
712 PtsFd,
713 PtmFd);
714 Result = -1;
715 goto ErrorExit;
716 }
717 }
718 else if ((Mode == SimpleReadWriteBackgroundSignal) || (Mode == SimpleReadWriteBackgroundSignalNoStop))
719 {
720
721 LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EINTR);
722 }
723 else
724 {
725 LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EIO);
726 }
727
728 //
729 // So far so good, now write a response from the subordinate.
730 //
731
732 LxtLogInfo("Subordinate(FD:%d) --> master(FD:%d):%*s", PtsFd, PtmFd, ReplyLength, Reply);
733
734 ExpectedResult = ReplyLength;
735 if (Mode != SimpleReadWriteBackgroundSignal)
736 {
737 LxtCheckErrno(BytesReadWrite = write(PtsFd, Reply, ExpectedResult));
738 LxtCheckFnResults("write", BytesReadWrite, ExpectedResult);
739 }
740 else
741 {
742 LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Reply, ExpectedResult), EINTR);
743 BytesReadWrite = ExpectedResult;
744 ExpectedResult = 0;
745 }
746
747 //
748 // Read from master.
749 //
750
751 LxtLogInfo("Reading from master");
752 memset(ReadBuffer, 0, sizeof(ReadBuffer));
753 if (Mode != SimpleReadWriteBackgroundSignal)
754 {
755 LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));
756 if (PacketMode != 0)
757 {
758 LxtCheckEqual(ReadBuffer[0], 0, "%hhd");
759 if (BytesReadWrite > 0)
760 {
761 BytesReadWrite -= 1;
762 }
763 }
764
765 ReadBufferMaster[BytesReadWrite] = '\0';
766 LxtLogInfo("Reply received by master(FD:%d):%s", PtmFd, ReadBufferMaster);
767 LxtLogInfo("Last character = %d [\\n = %d, \\r = %d]", ReadBufferMaster[BytesReadWrite - 1], '\n', '\r');
768 }
769 else
770 {
771 LxtCheckErrno(fcntl(PtmFd, F_SETFL, (PtmFlags | O_NONBLOCK)));
772 LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, BytesReadWrite), EAGAIN);
773 BytesReadWrite = 0;
774 }
775
776 LxtCheckFnResults("read", BytesReadWrite, ExpectedResult);
777
778 //
779 // Compare the messages.
780 //
781
782 if (memcmp(ReadBufferMaster, Reply, BytesReadWrite) != 0)
783 {
784 LxtLogError(
785 "Data read from master(FD:%d) does not match what was "
786 "written by subordinate(FD:%d).",
787 PtmFd,
788 PtsFd);
789
790 Result = -1;
791 goto ErrorExit;
792 }
793
794 ErrorExit:
795 fcntl(PtmFd, F_SETFL, PtmFlags);
796 return Result;
797 }
798
799 int TerminalSettingsGet(int Fd, cc_t* ControlArrayOut, tcflag_t* ControlFlagsOut, tcflag_t* InputFlagsOut, tcflag_t* LocalFlagsOut, tcflag_t* OutputFlagsOut)
800
801 /*++
802
803 Routine Description:
804
805 This routine will use termios to get the settings for the FD.
806 All of the *Out variables are optional.
807
808 Arguments:
809
810 Fd - Supplies the FD.
811
812 ControlArrayOut - Supplies an optional pointer to receive the NCCS element
813 control array.
814
815 ControlFlagsOut - Supplies an optional pointer to receive the terminal
816 control flags.
817
818 InputFlagsOut - Supplies an optional pointer to receive the terminal input
819 flags.
820
821 LocalFlagsOut - Supplies an optional pointer to receive the terminal local
822 flags.
823
824 OutputFlagsOut - Supplies an optional pointer to receive the terminal
825 output flags.
826
827 Return Value:
828
829 0 on success, error code on failure.
830
831 --*/
832
833 {
834
835 int Result;
836 struct termios Tios;
837
838 Result = tcgetattr(Fd, &Tios);
839 if (Result < 0)
840 {
841 goto ErrorExit;
842 }
843
844 if (ControlArrayOut != NULL)
845 {
846 memcpy(ControlArrayOut, Tios.c_cc, NCCS * sizeof(cc_t));
847 }
848
849 if (ControlFlagsOut != NULL)
850 {
851 *ControlFlagsOut = Tios.c_cflag;
852 }
853
854 if (InputFlagsOut != NULL)
855 {
856 *InputFlagsOut = Tios.c_iflag;
857 }
858
859 if (LocalFlagsOut != NULL)
860 {
861 *LocalFlagsOut = Tios.c_lflag;
862 }
863
864 if (OutputFlagsOut != NULL)
865 {
866 *OutputFlagsOut = Tios.c_oflag;
867 }
868
869 ErrorExit:
870 return Result;
871 }
872
873 int TerminalSettingsGetControlArray(int Fd, cc_t* ControlArrayOut)
874
875 /*++
876
877 Routine Description:
878
879 This routine will use termios to get the NCCS element control array.
880
881 Arguments:
882
883 Fd - Supplies the FD.
884
885 ControlArrayOut - Supplies a pointer to receive the NCCS element control
886 array.
887
888 Return Value:
889
890 0 on success, error code on failure.
891
892 --*/
893
894 {
895
896 return TerminalSettingsGet(Fd, ControlArrayOut, NULL, NULL, NULL, NULL);
897 }
898
899 int TerminalSettingsGetControlFlags(int Fd, tcflag_t* ControlFlagsOut)
900
901 /*++
902
903 Routine Description:
904
905 This routine will use termios to get the local flags for the FD.
906
907 Arguments:
908
909 Fd - Supplies the FD.
910
911 ControlFlagsOut - Supplies a pointer to receive the terminal control flags.
912
913 Return Value:
914
915 0 on success, error code on failure.
916
917 --*/
918
919 {
920
921 return TerminalSettingsGet(Fd, NULL, ControlFlagsOut, NULL, NULL, NULL);
922 }
923
924 int TerminalSettingsGetInputFlags(int Fd, tcflag_t* InputFlagsOut)
925
926 /*++
927
928 Routine Description:
929
930 This routine will use termios to get the input flags for the FD.
931
932 Arguments:
933
934 Fd - Supplies the FD.
935
936 InputFlagsOut - Supplies a pointer to receive the terminal input flags.
937
938 Return Value:
939
940 0 on success, error code on failure.
941
942 --*/
943
944 {
945
946 return TerminalSettingsGet(Fd, NULL, NULL, InputFlagsOut, NULL, NULL);
947 }
948
949 int TerminalSettingsGetLocalFlags(int Fd, tcflag_t* LocalFlagsOut)
950
951 /*++
952
953 Routine Description:
954
955 This routine will use termios to get the local flags for the FD.
956
957 Arguments:
958
959 Fd - Supplies the FD.
960
961 LocalFlagsOut - Supplies a pointer to receive the terminal local flags.
962
963 Return Value:
964
965 0 on success, error code on failure.
966
967 --*/
968
969 {
970
971 return TerminalSettingsGet(Fd, NULL, NULL, NULL, LocalFlagsOut, NULL);
972 }
973
974 int TerminalSettingsGetOutputFlags(int Fd, tcflag_t* OutputFlagsOut)
975
976 /*++
977
978 Routine Description:
979
980 This routine will use termios to get the output flags for the FD.
981
982 Arguments:
983
984 Fd - Supplies the FD.
985
986 OutputFlagsOut - Supplies a pointer to receive the terminal output flags.
987
988 Return Value:
989
990 0 on success, error code on failure.
991
992 --*/
993
994 {
995
996 return TerminalSettingsGet(Fd, NULL, NULL, NULL, NULL, OutputFlagsOut);
997 }
998
999 int TerminalSettingsSet(int Fd, cc_t* ControlArray, tcflag_t ControlFlags, tcflag_t InputFlags, tcflag_t LocalFlags, tcflag_t OutputFlags)
1000
1001 /*++
1002
1003 Routine Description:
1004
1005 This routine will use termios to update the settings for the FD.
1006
1007 Arguments:
1008
1009 Fd - Supplies the FD.
1010
1011 ControlArray - Supplies a pointer to the new NCCS element control array.
1012
1013 ControlFlags - Supplies the new terminal control flags.
1014
1015 InputFlagsOut - Supplies the new terminal input flags.
1016
1017 LocalFlagsOut - Supplies the new terminal local flags.
1018
1019 OutputFlagsOut - Supplies the new terminal output flags.
1020
1021 Return Value:
1022
1023 0 on success, error code on failure.
1024
1025 --*/
1026
1027 {
1028
1029 int Result;
1030 struct termios Tios = {0};
1031
1032 memcpy(Tios.c_cc, ControlArray, NCCS * sizeof(cc_t));
1033 Tios.c_cflag = ControlFlags;
1034 Tios.c_iflag = InputFlags;
1035 Tios.c_lflag = LocalFlags;
1036 Tios.c_oflag = OutputFlags;
1037 Result = tcsetattr(Fd, TCSANOW, &Tios);
1038 return Result;
1039 }
1040
1041 int TerminalSettingsSetControlArray(int Fd, cc_t* ControlArray)
1042
1043 /*++
1044
1045 Routine Description:
1046
1047 This routine will use termios to set the control array for the FD.
1048
1049 Arguments:
1050
1051 Fd - Supplies the FD.
1052
1053 ControlArray - Supplies the new control array.
1054
1055 Return Value:
1056
1057 0 on success, error code on failure.
1058
1059 --*/
1060
1061 {
1062
1063 int Result;
1064 struct termios Tios = {0};
1065
1066 Result = TerminalSettingsGet(Fd, NULL, &Tios.c_cflag, &Tios.c_iflag, &Tios.c_lflag, &Tios.c_oflag);
1067 if (Result < 0)
1068 {
1069 goto ErrorExit;
1070 }
1071
1072 Result = TerminalSettingsSet(Fd, ControlArray, Tios.c_cflag, Tios.c_iflag, Tios.c_lflag, Tios.c_oflag);
1073
1074 ErrorExit:
1075 return Result;
1076 }
1077
1078 int TerminalSettingsSetControlFlags(int Fd, tcflag_t ControlFlags)
1079
1080 /*++
1081
1082 Routine Description:
1083
1084 This routine will use termios to set the control flags for the FD.
1085
1086 Arguments:
1087
1088 Fd - Supplies the FD.
1089
1090 ControlFlags - Supplies the new control flags.
1091
1092 Return Value:
1093
1094 0 on success, error code on failure.
1095
1096 --*/
1097
1098 {
1099
1100 int Result;
1101 struct termios Tios = {0};
1102
1103 Result = TerminalSettingsGet(Fd, Tios.c_cc, NULL, &Tios.c_iflag, &Tios.c_lflag, &Tios.c_oflag);
1104 if (Result < 0)
1105 {
1106 goto ErrorExit;
1107 }
1108
1109 Result = TerminalSettingsSet(Fd, Tios.c_cc, ControlFlags, Tios.c_iflag, Tios.c_lflag, Tios.c_oflag);
1110
1111 ErrorExit:
1112 return Result;
1113 }
1114
1115 int TerminalSettingsSetInputFlags(int Fd, tcflag_t InputFlags)
1116
1117 /*++
1118
1119 Routine Description:
1120
1121 This routine will use termios to set the input flags for the FD.
1122
1123 Arguments:
1124
1125 Fd - Supplies the FD.
1126
1127 InputFlags - Supplies the new input flags.
1128
1129 Return Value:
1130
1131 0 on success, error code on failure.
1132
1133 --*/
1134
1135 {
1136
1137 int Result;
1138 struct termios Tios = {0};
1139
1140 Result = TerminalSettingsGet(Fd, Tios.c_cc, &Tios.c_cflag, NULL, &Tios.c_lflag, &Tios.c_oflag);
1141 if (Result < 0)
1142 {
1143 goto ErrorExit;
1144 }
1145
1146 Result = TerminalSettingsSet(Fd, Tios.c_cc, Tios.c_cflag, InputFlags, Tios.c_lflag, Tios.c_oflag);
1147
1148 ErrorExit:
1149 return Result;
1150 }
1151
1152 int TerminalSettingsSetLocalFlags(int Fd, tcflag_t LocalFlags)
1153
1154 /*++
1155
1156 Routine Description:
1157
1158 This routine will use termios to set the local flags for the FD.
1159
1160 Arguments:
1161
1162 Fd - Supplies the FD.
1163
1164 LocalFlags - Supplies the new local flags.
1165
1166 Return Value:
1167
1168 0 on success, error code on failure.
1169
1170 --*/
1171
1172 {
1173
1174 int Result;
1175 struct termios Tios = {0};
1176
1177 Result = TerminalSettingsGet(Fd, Tios.c_cc, &Tios.c_cflag, &Tios.c_iflag, NULL, &Tios.c_oflag);
1178 if (Result < 0)
1179 {
1180 goto ErrorExit;
1181 }
1182
1183 Result = TerminalSettingsSet(Fd, Tios.c_cc, Tios.c_cflag, Tios.c_iflag, LocalFlags, Tios.c_oflag);
1184
1185 ErrorExit:
1186 return Result;
1187 }
1188
1189 int TerminalSettingsSetOutputFlags(int Fd, tcflag_t OutputFlags)
1190
1191 /*++
1192
1193 Routine Description:
1194
1195 This routine will use termios to set the output flags for the FD.
1196
1197 Arguments:
1198
1199 Fd - Supplies the FD.
1200
1201 OutputFlags - Supplies the new output flags.
1202
1203 Return Value:
1204
1205 0 on success, error code on failure.
1206
1207 --*/
1208
1209 {
1210
1211 int Result;
1212 struct termios Tios = {0};
1213
1214 Result = TerminalSettingsGet(Fd, Tios.c_cc, &Tios.c_cflag, &Tios.c_iflag, &Tios.c_lflag, NULL);
1215 if (Result < 0)
1216 {
1217 goto ErrorExit;
1218 }
1219
1220 Result = TerminalSettingsSet(Fd, Tios.c_cc, Tios.c_cflag, Tios.c_iflag, Tios.c_lflag, OutputFlags);
1221
1222 ErrorExit:
1223 return Result;
1224 }
1225
1226 int WriteReadFdCommon(int WriteFd, size_t WriteSizes[], size_t NumWriteSizes, int ReadFd, size_t ReadSizes[], size_t NumReadSizes)
1227
1228 /*++
1229
1230 Routine Description:
1231
1232 This routine performs write operation of given sizes to the WriteFd and
1233 reads from the ReadFd for the specified sizes. Each write is expected to
1234 succeed for the given size and each read is expected to succeed for the
1235 given size. This routine will also validate that the data read aligns up
1236 with the data written. All the writes will be performed before any of the
1237 reads.
1238
1239 Arguments:
1240
1241 WriteFd - Supplies the FD on which the write operation should be called.
1242
1243 WriteSizes - Supplies the array of sizes for the write operation..
1244
1245 NumWriteSizes - Supplies the number of elements in the write size array.
1246
1247 ReadFd - Supplies the FD on which the read operation should be called.
1248
1249 ReadSizes - Supplies the array of sizes for the read operation..
1250
1251 NumReadSizes - Supplies the number of elements in the read size array.
1252
1253 Return Value:
1254
1255 0 on success, error code on failure.
1256 --*/
1257
1258 {
1259
1260 char* AccumulatedReadBuffer;
1261 char* AccumulatedWriteBuffer;
1262 ssize_t BytesReadWrite;
1263 size_t Itr;
1264 size_t Offset;
1265 char* ReadBuffer;
1266 int Result;
1267 size_t TotalReadSize;
1268 size_t TotalWriteSize;
1269 char* WriteBuffer;
1270
1271 AccumulatedReadBuffer = NULL;
1272 AccumulatedWriteBuffer = NULL;
1273 ReadBuffer = NULL;
1274 WriteBuffer = NULL;
1275 TotalWriteSize = 0;
1276 TotalReadSize = 0;
1277
1278 //
1279 // Calculate the size of total number of bytes to be written and read.
1280 //
1281
1282 for (Itr = 0; Itr < NumWriteSizes; Itr += 1)
1283 {
1284 TotalWriteSize += WriteSizes[Itr];
1285 }
1286
1287 for (Itr = 0; Itr < NumReadSizes; Itr += 1)
1288 {
1289 TotalReadSize += ReadSizes[Itr];
1290 }
1291
1292 //
1293 // Allocate memory to hold the data for the accumulated writes and reads.
1294 //
1295
1296 AccumulatedWriteBuffer = calloc(TotalWriteSize, 1);
1297 if (AccumulatedWriteBuffer == NULL)
1298 {
1299 LxtLogError("Failed to allocate memory for Write Buffer");
1300 Result = LXT_RESULT_FAILURE;
1301 goto ErrorExit;
1302 }
1303
1304 AccumulatedReadBuffer = calloc(TotalReadSize, 1);
1305 if (AccumulatedReadBuffer == NULL)
1306 {
1307 LxtLogError("Failed to allocate memory for Read Buffer");
1308 Result = LXT_RESULT_FAILURE;
1309 goto ErrorExit;
1310 }
1311
1312 //
1313 // First perform all the writes.
1314 //
1315
1316 Offset = 0;
1317 for (Itr = 0; Itr < NumWriteSizes; Itr += 1)
1318 {
1319 WriteBuffer = calloc(WriteSizes[Itr], 1);
1320 if (WriteBuffer == NULL)
1321 {
1322 Result = ENOMEM;
1323 goto ErrorExit;
1324 }
1325
1326 LxtCheckErrno(GetRandomMessage(WriteBuffer, WriteSizes[Itr], FALSE));
1327 LxtCheckErrno(BytesReadWrite = write(WriteFd, WriteBuffer, WriteSizes[Itr]));
1328
1329 LxtCheckFnResults("write", BytesReadWrite, (ssize_t)WriteSizes[Itr]);
1330 memcpy(&AccumulatedWriteBuffer[Offset], WriteBuffer, WriteSizes[Itr]);
1331
1332 Offset += WriteSizes[Itr];
1333 free(WriteBuffer);
1334 WriteBuffer = NULL;
1335 }
1336
1337 //
1338 // On Ubuntu16 pty processing is asynchronous so pause a second to give the
1339 // writes time to be processed.
1340 //
1341
1342 sleep(1);
1343
1344 //
1345 // Now read the data previously written.
1346 //
1347
1348 Offset = 0;
1349 for (Itr = 0; Itr < NumReadSizes; Itr += 1)
1350 {
1351 ReadBuffer = calloc(ReadSizes[Itr], 1);
1352 if (ReadBuffer == NULL)
1353 {
1354 Result = ENOMEM;
1355 goto ErrorExit;
1356 }
1357
1358 LxtCheckErrno(BytesReadWrite = read(ReadFd, ReadBuffer, ReadSizes[Itr]));
1359
1360 LxtCheckFnResults("read", BytesReadWrite, (ssize_t)ReadSizes[Itr]);
1361 memcpy(&AccumulatedReadBuffer[Offset], ReadBuffer, ReadSizes[Itr]);
1362 Offset += ReadSizes[Itr];
1363 free(ReadBuffer);
1364 ReadBuffer = NULL;
1365 }
1366
1367 //
1368 // Data read should align up with the previously written data.
1369 //
1370
1371 if (memcmp(AccumulatedWriteBuffer, AccumulatedReadBuffer, min(TotalWriteSize, TotalReadSize)) != 0)
1372 {
1373
1374 LxtLogError(
1375 "Data read from FD:%d does not match what was "
1376 "written by FD:%d.",
1377 ReadFd,
1378 WriteFd);
1379
1380 Result = -1;
1381 goto ErrorExit;
1382 }
1383
1384 /*
1385 LxtLogInfo("Data Written: ");
1386 DumpBuffer(AccumulatedWriteBuffer, TotalWriteSize);
1387 LxtLogInfo("Data Read: ");
1388 DumpBuffer(AccumulatedReadBuffer, TotalReadSize);
1389 */
1390
1391 ErrorExit:
1392
1393 if (AccumulatedReadBuffer != NULL)
1394 {
1395 free(AccumulatedReadBuffer);
1396 }
1397
1398 if (AccumulatedWriteBuffer != NULL)
1399 {
1400 free(AccumulatedWriteBuffer);
1401 }
1402
1403 if (ReadBuffer != NULL)
1404 {
1405 free(ReadBuffer);
1406 }
1407
1408 if (WriteBuffer != NULL)
1409 {
1410 free(WriteBuffer);
1411 }
1412
1413 return Result;
1414 }