| /* Demo leapsecond deadlock | |
| * by: John Stultz (john.stultz@linaro.org) | |
| * (C) Copyright IBM 2012 | |
| * (C) Copyright 2013, 2015 Linaro Limited | |
| * Licensed under the GPL | |
| * | |
| * This test demonstrates leapsecond deadlock that is possibe | |
| * on kernels from 2.6.26 to 3.3. | |
| * | |
| * WARNING: THIS WILL LIKELY HARDHANG SYSTEMS AND MAY LOSE DATA | |
| * RUN AT YOUR OWN RISK! | |
| * To build: | |
| * $ gcc leapcrash.c -o leapcrash -lrt | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <time.h> | |
| #include <sys/time.h> | |
| #include <sys/timex.h> | |
| #include <string.h> | |
| #include <signal.h> | |
| #ifdef KTEST | |
| #include "../kselftest.h" | |
| #else | |
| static inline int ksft_exit_pass(void) | |
| { | |
| exit(0); | |
| } | |
| static inline int ksft_exit_fail(void) | |
| { | |
| exit(1); | |
| } | |
| #endif | |
| /* clear NTP time_status & time_state */ | |
| int clear_time_state(void) | |
| { | |
| struct timex tx; | |
| int ret; | |
| /* | |
| * We have to call adjtime twice here, as kernels | |
| * prior to 6b1859dba01c7 (included in 3.5 and | |
| * -stable), had an issue with the state machine | |
| * and wouldn't clear the STA_INS/DEL flag directly. | |
| */ | |
| tx.modes = ADJ_STATUS; | |
| tx.status = STA_PLL; | |
| ret = adjtimex(&tx); | |
| tx.modes = ADJ_STATUS; | |
| tx.status = 0; | |
| ret = adjtimex(&tx); | |
| return ret; | |
| } | |
| /* Make sure we cleanup on ctrl-c */ | |
| void handler(int unused) | |
| { | |
| clear_time_state(); | |
| exit(0); | |
| } | |
| int main(void) | |
| { | |
| struct timex tx; | |
| struct timespec ts; | |
| time_t next_leap; | |
| int count = 0; | |
| setbuf(stdout, NULL); | |
| signal(SIGINT, handler); | |
| signal(SIGKILL, handler); | |
| printf("This runs for a few minutes. Press ctrl-c to stop\n"); | |
| clear_time_state(); | |
| /* Get the current time */ | |
| clock_gettime(CLOCK_REALTIME, &ts); | |
| /* Calculate the next possible leap second 23:59:60 GMT */ | |
| next_leap = ts.tv_sec; | |
| next_leap += 86400 - (next_leap % 86400); | |
| for (count = 0; count < 20; count++) { | |
| struct timeval tv; | |
| /* set the time to 2 seconds before the leap */ | |
| tv.tv_sec = next_leap - 2; | |
| tv.tv_usec = 0; | |
| if (settimeofday(&tv, NULL)) { | |
| printf("Error: You're likely not running with proper (ie: root) permissions\n"); | |
| return ksft_exit_fail(); | |
| } | |
| tx.modes = 0; | |
| adjtimex(&tx); | |
| /* hammer on adjtime w/ STA_INS */ | |
| while (tx.time.tv_sec < next_leap + 1) { | |
| /* Set the leap second insert flag */ | |
| tx.modes = ADJ_STATUS; | |
| tx.status = STA_INS; | |
| adjtimex(&tx); | |
| } | |
| clear_time_state(); | |
| printf("."); | |
| } | |
| printf("[OK]\n"); | |
| return ksft_exit_pass(); | |
| } |