/* * interrupted_syscall.c * * Testtool for UML * This tool tests several cases of interrupted syscalls and * checks for the syscall restarting being correctly done. * * Copyright (C) 2004 Fujitsu Siemens Computers GmbH * Author: Bodo Stroesser (bodo.stroesser@fujitsu-siemens.com) * * Licensed under the GPL */ #include #include #include #include #include #include #include #include int child, tester; int traced; int pipe_fd[2]; volatile int * counterp; char outbuf[512], *p; void sighdlr( int sig) { if ( ! traced ) { printf("Signal %d caught\n", sig); } } void alarm_writer( int parent, int count) { while ( count-- ) { sleep( 1); if ( kill( parent, SIGALRM) ) { perror("kill( parent, SIGALRM)"); exit(1); } } if ( pipe_fd[0] == -1 ) goto out; sleep( 1); close( pipe_fd[0]); if ( write( pipe_fd[1], "X", 1) != 1 ) { perror("write( pipe_fd[1], \"X\", 1)"); exit(1); } out: sleep(60); exit(0); } void prepare( int mkpipe, int count, void * hdlr, int flags) { struct sigaction sa; int ret; int pid = getpid(); if ( mkpipe ) { if ( pipe( pipe_fd) ) { perror("pipe( pipe_fd)"); exit(1); } } else pipe_fd[0] = pipe_fd[1] = -1; memset( &sa, 0, sizeof( sa)); sa.sa_handler = hdlr; sa.sa_flags = flags; if ( sigaction( SIGALRM, &sa, NULL) ) { perror("sigaction( SIGALRM, &sa)"); exit(1); } if ( (child = fork()) < 0 ) { perror("fork()"); exit(1); } else if ( !child ) alarm_writer( pid, count); /* Child will never return */ if ( mkpipe ) { close( pipe_fd[1]); pipe_fd[1] = -1; } counterp[0] = 0; counterp[1] = 0; if ( hdlr == SIG_IGN ) counterp[2] = 0; else counterp[2] = 1; p = outbuf; } void cleanup( int ok, int syscalls, int signals) { int status; int syscall_count = counterp[0]; int signal_count = counterp[1]; printf( outbuf); if ( pipe_fd[0] != -1 ) close( pipe_fd[0]); if ( pipe_fd[1] != -1 ) close( pipe_fd[1]); kill( child, SIGKILL); waitpid( child, &status, 0); if ( traced ) { printf("Syscalls: %d, Signals: %d ==> ", syscall_count, signal_count); if ( syscall_count != syscalls || signal_count != signals ) { ok = 0; printf(" ERROR!\nExpected: Syscalls: %d Signals: %d\n", syscalls, signals); } else printf("OK.\n"); } if ( ! ok ) { printf("Test failed!\n"); exit(1); } printf("Test succeeded\n\n"); } int tester_fn( void) { struct timespec ts, ts2; sigset_t set; char buf[10]; int ret, errno_sav; sigprocmask( SIG_BLOCK, NULL, &set); printf("\n=====> Test case 1: nanosleep() 3.5 seconds, SIGALRM after 1 and 2 seconds, SIG_IGN\n"); prepare( 0, 2, SIG_IGN, 0); ts = (struct timespec){ 3, 500000000}; ret = nanosleep( &ts, &ts2); if ( ret ) { p+=sprintf(p, "ERROR: nanosleep(): %s", strerror( errno)); p+=sprintf(p, "; remaining time=%ld/%ld\n", ts2.tv_sec, ts2.tv_nsec); } else p+=sprintf(p, "OK: nanosleep() returns OK.\n"); cleanup( ret == 0 , 6, 2); printf("\n=====> Test case 2: nanosleep() 3.5 seconds, SIGALRM after 1 and 2 seconds, signal handler\n"); prepare( 0, 2, sighdlr, 0); ts = (struct timespec){ 3, 500000000}; ret = nanosleep( &ts, &ts2); if ( ret ) { p+=sprintf(p, "OK: nanosleep(): %s", strerror( errno)); p+=sprintf(p, "; remaining time=%ld/%ld\n", ts2.tv_sec, ts2.tv_nsec); } else p+=sprintf(p, "ERROR: nanosleep() returns OK.\n"); cleanup( ret && ts2.tv_sec == 2 , 4, 1); printf("\n=====> Test case 3: read(), SIGALRM after 1 and 2 seconds, 1 Byte after 3rd second, SIG_IGN\n"); prepare( 1, 2, SIG_IGN, 0); ret = read( pipe_fd[0], buf, sizeof( buf)); if ( ret != 1 ) p+=sprintf(p, "ERROR: read( pipe_fd[0], buf, sizeof(buf)): %s\n", strerror( errno)); else p+=sprintf(p, "OK: read received %d bytes\n", ret); cleanup( ret == 1 , 6, 2); printf("\n=====> Test case 4: read(), SIGALRM after 1 and 2 seconds, 1 Byte after 3rd second, signal handler, SA_RESTART\n"); prepare( 1, 2, sighdlr, SA_RESTART); ret = read( pipe_fd[0], buf, sizeof( buf)); if ( ret != 1 ) p+=sprintf(p, "ERROR: read( pipe_fd[0], buf, sizeof(buf)): %s\n", strerror( errno)); else p+=sprintf(p, "OK: read received %d bytes\n", ret); cleanup( ret == 1 , 10, 2); printf("\n=====> Test case 5: read(), SIGALRM after 1 second, 1 Byte after 2nd second, signal handler, ! SA_RESTART\n"); prepare( 1, 1, sighdlr, 0); ret = read( pipe_fd[0], buf, sizeof( buf)); errno_sav = errno; if ( ret != 1 ) p+=sprintf(p, "OK: read( pipe_fd[0], buf, sizeof(buf)): %s\n", strerror( errno)); else p+=sprintf(p, "ERROR: read received %d bytes\n", ret); cleanup( ret == -1 && errno_sav == EINTR , 4, 1); printf("\n=====> Test case 6: sigsuspend(), SIGALRM after 1 second, signal handler, SA_RESTART\n"); prepare( 0, 1, sighdlr, SA_RESTART); ret = sigsuspend( &set); errno_sav = errno; if ( ret < 0 ) p+=sprintf(p, "OK: sigsuspend(): %s\n", strerror( errno)); else p+=sprintf(p, "ERROR: sigsuspend returns OK.\n"); cleanup( ret == -1 && errno_sav == EINTR , 4, 1); } int main( int argc, char **argv) { int status, ret, sig, debug; sigset_t set; sigprocmask( SIG_BLOCK, NULL, &set); sigdelset( &set, SIGCHLD); sigprocmask( SIG_SETMASK, NULL, &set); counterp = mmap( NULL, 4096, PROT_WRITE|PROT_READ, MAP_SHARED|MAP_ANONYMOUS, 0, 0); if ( counterp == MAP_FAILED ) { perror("mmap"); exit(1); } debug = argc > 1 && !strcmp("debug",argv[1]); tester = fork(); if ( tester < 0 ) { perror("fork()"); exit(1); } if ( ! tester ) { printf("\n\nStarting Test: UNTRACED\n\n"); traced = 0; tester_fn(); if ( ptrace( PTRACE_TRACEME, 0, (void *)0, (void *)0) ) { perror("ptrace( PTRACE_TRACEME, 0, 0, 0)"); exit(1); } printf("\nStarting Test: TRACED\n"); traced = 1; kill( getpid(), SIGTRAP); tester_fn(); exit(0); } while (1) { ret = waitpid( tester, &status, 0); if ( ret != tester ) { fprintf( stderr, "Tracer: "); perror("waitpid"); exit(1); } if ( WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP ) { sig = 0; counterp[0]++; if ( debug ) printf("TRAP: syscall-# = %d, count = %d\n", ptrace( PTRACE_PEEKUSER, tester, (void *)(ORIG_EAX*4), NULL), counterp[0]); } else if ( WIFSTOPPED(status) && WSTOPSIG(status) == SIGALRM ) { if ( debug ) printf("SIGCHLD !!!!!\n"); sig = SIGALRM; if ( counterp[2] ) printf("Signal %d caught\n", sig); else printf("Signal %d ignored\n", sig); counterp[1]++; } else if ( WIFSTOPPED(status) && WSTOPSIG(status) == SIGCHLD ) { if ( debug ) printf("SIGCHLD !!!!!\n"); sig = SIGCHLD; } else { printf("\nTracer: Tester's status is %x: exiting\n", status); return (status != 0); } if ( ptrace( PTRACE_SYSCALL, tester, (void *)0, sig) < 0 ) { fprintf( stderr, "Parent: "); perror("ptrace( PTRACE_SYSCALL, tester, 0, sig)"); exit(1); } } }