#include #include #include #include #include "test.h" #include "tests.h" typedef struct priv PRIV; struct priv { int first_r_d; int lfd; int cfd; int lcmd[2]; int ccmd[2]; } ; #define PART 500 #define MAX (PART*4) static unsigned char data1[MAX]; static unsigned char data2[MAX]; static void run_dead_peer_run_l(void *pv) { PRIV *p; pid_t mypid; int fd; int fd2; int i; unsigned char c; int d1o; int dn; int xr; int xe; int e; p = pv; close(p->cfd); close(p->lcmd[0]); close(p->ccmd[0]); close(p->ccmd[1]); mypid = getpid(); printf("dead-peer: listener is %d\n",(int)mypid); printf("dead-peer: listener establishing connection\n"); mustwrite(p->lfd,&mypid,sizeof(pid_t)); sleep(1); SET_PIDCONN(fd,PIDCONN_LISTEN,0,0); mustwrite(p->lfd,"",1); SET_PIDCONN(fd2,PIDCONN_ACCEPT,fd,0); mustwrite(p->lfd,"",1); mustread(p->lfd,&c,1); sleep(1); write(p->lcmd[1],"",1); d1o = 0; while (1) { i = read(p->lcmd[1],&c,1); if (i < 0) { printf("dead-peer: listener command read: %s\n",strerror(errno)); test_fail(); } if (i == 0) break; switch (c) { default: printf("dead-peer: unrecognized listener command %02x\n",c); test_fail(); break; case 'c': close(fd2); break; case '1': dn = PART; if (0) { case '2': dn = PART * 2; } if (d1o+dn > MAX) { printf("dead-peer: listener data overrun (%d+%d > %d)\n",d1o,dn,MAX); test_fail(); } mustread(p->lcmd[1],&c,1); switch (c) { default: printf("dead-peer: bad listener expect char %02x\n",c); test_fail(); break; case '1': xr = PART; break; case '2': xr = PART*2; break; case 'p': xr = -1; xe = ENOLINK; break; } i = write(fd2,&data1[d1o],dn); e = errno; if (i < 0) { if (xr >= 0) { printf("dead-peer: write %d at %d failed: %s\n",dn,d1o,strerror(e)); test_fail(); } if (e != xe) { // two printfs because strerror() has a single buffer printf("dead-peer: write %d at %d failed %d [%s]",dn,d1o,e,strerror(e)); printf(", expected %d [%s]\n",xe,strerror(xe)); test_fail(); } } else if (i != xr) { if (xr < 0) { printf("dead-peer: write %d at %d returned %d, expected error %d [%s]\n",dn,d1o,i,xe,strerror(xe)); } else { printf("dead-peer: write %d at %d returned %d, expected %d\n",dn,d1o,i,xr); } test_fail(); } d1o += dn; break; } write(p->lcmd[1],"",1); } mustwrite(p->lfd,"",1); mustread(p->lfd,&c,1); printf("dead-peer: listener done\n"); sleep(1); } static void run_dead_peer_run_c(void *pv) { PRIV *p; pid_t lpid; int fd; char c; int i; int d2o; int dn; int xr; int xe; int defer; int o; int e; p = pv; close(p->lfd); close(p->lcmd[0]); close(p->ccmd[0]); close(p->lcmd[1]); printf("dead-peer: connecter is %d\n",(int)getpid()); mustread(p->cfd,&lpid,sizeof(pid_t)); printf("dead-peer: connecter establishing connection to %d\n",(int)lpid); sleep(1); mustread(p->cfd,&c,1); SET_PIDCONN(fd,PIDCONN_CONNECT,0,lpid); mustread(p->cfd,&c,1); mustwrite(p->cfd,"",1); sleep(1); d2o = 0; write(p->ccmd[1],"",1); while (1) { i = read(p->ccmd[1],&c,1); if (i < 0) { printf("dead-peer: connecter command read: %s\n",strerror(errno)); test_fail(); } if (i == 0) break; switch (c) { default: printf("dead-peer: unrecognized connecter command %02x\n",c); test_fail(); break; case 'c': close(fd); break; case '1': dn = PART; if (0) { case '2': dn = PART * 2; } if (d2o+dn > MAX) { printf("dead-peer: connecter data overrun (%d+%d > %d)\n",d2o,dn,MAX); test_fail(); } mustread(p->ccmd[1],&c,1); defer = 0; switch (c) { default: printf("dead-peer: bad connecter expect char %02x\n",c); test_fail(); break; case '-': defer = 1; break; case '0': xr = 0; break; case '1': xr = PART; break; case '2': xr = PART*2; break; case 'p': xr = -1; xe = ENOLINK; break; } i = read(fd,&data2[d2o],dn); e = errno; if (defer) { mustread(p->ccmd[1],&c,1); switch (c) { default: printf("dead-peer: bad connecter deferred expect char %02x\n",c); test_fail(); break; case '0': xr = 0; break; case '1': xr = PART; break; case '2': xr = PART*2; break; } } if (i < 0) { if (xr >= 0) { printf("dead-peer: read %d at %d failed: %s\n",dn,d2o,strerror(e)); test_fail(); } if (e != xe) { // two printfs because strerror() has a single buffer printf("dead-peer: read %d at %d failed %d [%s]",dn,d2o,e,strerror(e)); printf(", expected %d [%s]\n",xe,strerror(xe)); test_fail(); } } else if (i != xr) { if (xr < 0) { printf("dead-peer: read %d at %d returned %d, expected error %d [%s]\n",dn,d2o,i,xe,strerror(xe)); } else { printf("dead-peer: read %d at %d returned %d, expected %d\n",dn,d2o,i,xr); } test_fail(); } if (xr > 0) { i = 0; for (o=0;o= 10) { printf("...more\n"); break; } printf("wrote [%d] = %02x, read [%d] = %02x\n", o,data1[d2o+o],o,data2[d2o+o]); i ++; } } if (i) test_fail(); d2o += xr; } break; } write(p->ccmd[1],"",1); } mustwrite(p->cfd,"",1); mustread(p->cfd,&c,1); printf("dead-peer: connecter done\n"); sleep(1); } static void do_dead_peer(const char *ops) { PRIV p; KID *kl; KID *kc; char c; printf("dead-peer: >>>>>>>> ops = %s\n",ops); local_socketpair(&p.lfd,&p.cfd); local_socketpair(&p.lcmd[0],&p.lcmd[1]); local_socketpair(&p.ccmd[0],&p.ccmd[1]); printf("dead-peer: lfd %d cfd %d lcmd [%d %d] ccmd [%d %d]\n", p.lfd, p.cfd, p.lcmd[0], p.lcmd[1], p.ccmd[0], p.ccmd[1]); kl = fork_kid(&run_dead_peer_run_l,&p); kc = fork_kid(&run_dead_peer_run_c,&p); close(p.lfd); close(p.cfd); close(p.lcmd[1]); close(p.ccmd[1]); mustread(p.lcmd[0],&c,1); mustread(p.ccmd[0],&c,1); while <"cmds"> (1) { switch (*ops) { case '\0': break <"cmds">; case 'w': switch (ops[1]) { case '1': case '2': switch (ops[2]) { case '1': case '2': case 'p': mustwrite(p.lcmd[0],&ops[1],2); mustread(p.lcmd[0],&c,1); ops += 3; break; default: abort(); break; } break; case 'c': mustwrite(p.lcmd[0],"c",1); mustread(p.lcmd[0],&c,1); ops += 2; break; default: abort(); break; } break; case 'r': switch (ops[1]) { case '1': case '2': switch (ops[2]) { case '0': case '1': case '2': mustwrite(p.ccmd[0],&ops[1],2); mustread(p.ccmd[0],&c,1); ops += 3; break; default: abort(); break; } break; case 'c': mustwrite(p.ccmd[0],"c",1); mustread(p.ccmd[0],&c,1); ops += 2; break; default: abort(); break; } break; case 'R': switch (ops[1]) { case '1': case '2': mustwrite(p.ccmd[0],&ops[1],1); mustwrite(p.ccmd[0],"-",1); ops += 2; break; case '-': switch (ops[2]) { case '0': case '1': case '2': mustwrite(p.ccmd[0],&ops[2],1); mustread(p.ccmd[0],&c,1); ops += 3; break; default: abort(); break; } break; default: abort(); break; } } } close(p.lcmd[0]); close(p.ccmd[0]); reap_kid(kl); reap_kid(kc); } static void setup_data(void) { int i; srandom(1); for (i=MAX-1;i>=0;i--) data1[i] = (random() >> 22) & 0xff; } static void run_dead_peer(void) { setup_data(); do_dead_peer("w22wcr11r21r20"); if (! test_failed()) do_dead_peer("w11R2wcR-1r10"); if (! test_failed()) do_dead_peer("w22r11rcw1p"); printf("dead-peer: done\n"); } const TEST test_dead_peer = { &run_dead_peer };