added cvs_history
[tridge/junkcode.git] / sshtest.c
1 /*
2   tridge@linuxcare.com January 2000
3
4   This is a sample program that demonstrates a deadlock in sshd when
5   handling large amounts of bi-directional data. This deadlock has
6   been the cause of lots of problems with rsync over ssh. Although the
7   problem only maniifests itself occasionally it makes large rsync
8   over ssh transfers unreliable.
9
10   Luckily the fix is very easy - tell sshd to use socketpair() instead
11   of pipe(). Just remove the line:
12         #define USE_PIPES 1
13   from near the bottom of includes.h in the ssh sources.
14
15   Note that fixing the problem by playing with the write sizes or by
16   using non-blocking writes due to the following bugs in various OSes.
17
18   - in Linux 2.2 a write to a non-blocking pipe will either return
19   EAGAIN if no space is avaiilable or will block if not. This is fixed
20   in Linux 2.3.
21
22   - Under IRIX and OSF1 a select on a blocking pipe may return before
23   PIPE_BUF can be written. When a subsequent write of more than 1 byte
24   is done the write blocks.
25
26   - under some versions of OpenBSD a select on a non-blocking pipe
27   will always return immediately, even when no space is available.
28
29   The sum of all these factors means it is not possible to write
30   portable code that uses pipes in a way that won't allow deadlocks,
31   unless writes are restricted to a single byte after a select on a
32   blocking pipe. The performance of that solution would be terrible.
33
34 running this code:
35
36   1) "make nettest"
37   2) nettest "ssh localhost nettest -s"
38   3) if it blocks then you have hit the deadlock.
39
40 */
41
42
43 #include <stdio.h>
44 #include <unistd.h>
45 #include <errno.h>
46 #include <string.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/socket.h>
50 #include <stdlib.h>
51
52 #define TOTAL_SIZE (10*1024*1024)
53
54
55 static int fd_pair(int fd[2])
56 {
57         return socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
58 }
59
60
61 static int write_loop(int fd, char *buf, int size)
62 {
63         int total=0;
64
65         while (size) {
66                 int n = write(fd, buf, size);
67                 if (n <= 0) break;
68                 size -= n;
69                 buf += n;
70                 total += n;
71         }
72         return total;
73 }
74
75 static int piped_child(char *command,int *f_in,int *f_out)
76 {
77         int pid;
78         int to_child_pipe[2];
79         int from_child_pipe[2];
80
81         if (fd_pair(to_child_pipe) < 0 ||
82             fd_pair(from_child_pipe) < 0) {
83                 fprintf(stderr,"fd_pair: %s\n",strerror(errno));
84                 exit(1);
85         }
86
87
88         pid = fork();
89         if (pid < 0) {
90                 fprintf(stderr,"fork: %s\n",strerror(errno));
91                 exit(1);
92         }
93
94         if (pid == 0) {
95                 if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
96                     close(to_child_pipe[1]) < 0 ||
97                     close(from_child_pipe[0]) < 0 ||
98                     dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
99                         fprintf(stderr,"Failed to dup/close : %s\n",strerror(errno));
100                         exit(1);
101                 }
102                 if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]);
103                 if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]);
104                 system(command);
105                 exit(1);
106         }
107
108         if (close(from_child_pipe[1]) < 0 ||
109             close(to_child_pipe[0]) < 0) {
110                 fprintf(stderr,"Failed to close : %s\n",strerror(errno));   
111                 exit(1);
112         }
113
114         *f_in = from_child_pipe[0];
115         *f_out = to_child_pipe[1];
116
117         return pid;
118 }
119
120 static void sender(int fin, int fout)
121 {
122         int n;
123         char buf[1024];
124         int total = 0;
125         
126         while (total < TOTAL_SIZE) {
127                 n = read(fin, buf, sizeof(buf));
128                 if (n <= 0) {
129                         fprintf(stderr,"write error in sender at %d\n", total);
130                         break;
131                 }
132                 write_loop(fout, buf, n);
133                 total += n;
134                 fprintf(stderr, "-");
135         }
136         fprintf(stderr, "sender done\n");
137 }
138
139 static void generator(int fd)
140 {
141         int n;
142         char buf[1024];
143         int total=0;
144
145         while (total < TOTAL_SIZE) {
146                 n = 1 + random() % (sizeof(buf)-1);
147                 n = write_loop(fd, buf, n);
148                 if (n <= 0) {
149                         fprintf(stderr,"write error in generator at %d\n", total);
150                         break;
151                 }
152                 total += n;
153                 fprintf(stderr, "*");
154         }
155         fprintf(stderr, "generator done\n");
156 }
157
158 static void receiver(int fd)
159 {
160         ssize_t n;
161         ssize_t total=0;
162         char buf[1024];
163
164         while (total < TOTAL_SIZE) {
165                 n = read(fd, buf, sizeof(buf));
166                 if (n <= 0) {
167                         fprintf(stderr,"read error in receiver\n");
168                         break;
169                 }
170                 total += n;
171                 fprintf(stderr, "+");
172         }
173         fprintf(stderr, "receiver done\n");
174 }
175
176 int main(int argc, char *argv[])
177 {
178         int c, f_in, f_out;
179         int am_sender = 0;
180
181         while ((c = getopt(argc, argv, "s")) != -1) {
182                 switch (c){
183                 case 's':
184                         am_sender = 1;
185                         break;
186                 }
187         }
188         
189         if (am_sender) {
190                 sender(0, 1);
191         } else {
192                 char *command = argv[1];
193                 printf("running %s\n", command);
194                 piped_child(command, &f_in, &f_out);
195                 if (fork()) {
196                         generator(f_out);
197                 } else {
198                         receiver(f_in);
199                 }
200         }
201
202         return 0;
203 }