diff --git a/src/basic/random-util.c b/src/basic/random-util.c index a9fe25432c..9423a0805d 100644 --- a/src/basic/random-util.c +++ b/src/basic/random-util.c @@ -30,183 +30,142 @@ #include "missing_syscall.h" #include "parse-util.h" #include "random-util.h" -#include "siphash24.h" +#include "sha256.h" #include "time-util.h" -static bool srand_called = false; - -int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { - static int have_syscall = -1; - _cleanup_close_ int fd = -1; - - /* Gathers some high-quality randomness from the kernel. This call won't block, unless the RANDOM_BLOCK - * flag is set. If it doesn't block, it will still always return some data from the kernel, regardless - * of whether the random pool is fully initialized or not. When creating cryptographic key material you - * should always use RANDOM_BLOCK. */ - - if (n == 0) - return 0; - - /* Use the getrandom() syscall unless we know we don't have it. */ - if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { - for (;;) { - ssize_t l = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_INSECURE); - - if (l > 0) { - have_syscall = true; - - if ((size_t) l == n) - return 0; /* Yay, success! */ - - /* We didn't get enough data, so try again */ - assert((size_t) l < n); - p = (uint8_t*) p + l; - n -= l; - continue; - - } else if (l == 0) { - have_syscall = true; - return -EIO; - - } else if (ERRNO_IS_NOT_SUPPORTED(errno)) { - /* We lack the syscall, continue with reading from /dev/urandom. */ - have_syscall = false; - break; - - } else if (errno == EINVAL) { - /* If we previously passed GRND_INSECURE, and this flag isn't known, then - * we're likely running an old kernel which has getrandom() but not - * GRND_INSECURE. In this case, fall back to /dev/urandom. */ - if (!FLAGS_SET(flags, RANDOM_BLOCK)) - break; - - return -errno; - } else - return -errno; - } - } - - fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return errno == ENOENT ? -ENOSYS : -errno; - - return loop_read_exact(fd, p, n, true); -} - -static void clear_srand_initialization(void) { - srand_called = false; -} - -void initialize_srand(void) { - static bool pthread_atfork_registered = false; - unsigned x; - - if (srand_called) - return; +/* This is a "best effort" kind of thing, but has no real security value. + * So, this should only be used by random_bytes(), which is not meant for + * crypto. This could be made better, but we're *not* trying to roll a + * userspace prng here, or even have forward secrecy, but rather just do + * the shortest thing that is at least better than libc rand(). */ +static void fallback_random_bytes(void *p, size_t n) { + static thread_local uint64_t fallback_counter = 0; + struct { + char label[32]; + uint64_t call_id, block_id; + usec_t stamp_mono, stamp_real; + pid_t pid, tid; + uint8_t auxval[16]; + } state = { + /* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */ + .label = "systemd fallback random bytes v1", + .call_id = fallback_counter++, + .stamp_mono = now(CLOCK_MONOTONIC), + .stamp_real = now(CLOCK_REALTIME), + .pid = getpid(), + .tid = gettid() + }; #if HAVE_SYS_AUXV_H - /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed - * the pseudo-random generator. It's better than nothing... But let's first hash it to make it harder - * to recover the original value by watching any pseudo-random bits we generate. After all the - * AT_RANDOM data might be used by other stuff too (in particular: ASLR), and we probably shouldn't - * leak the seed for that. */ - - const void *auxv = ULONG_TO_PTR(getauxval(AT_RANDOM)); - if (auxv) { - static const uint8_t auxval_hash_key[16] = { - 0x92, 0x6e, 0xfe, 0x1b, 0xcf, 0x00, 0x52, 0x9c, 0xcc, 0x42, 0xcf, 0xdc, 0x94, 0x1f, 0x81, 0x0f - }; - - x = (unsigned) siphash24(auxv, 16, auxval_hash_key); - } else -#endif - x = 0; - - x ^= (unsigned) now(CLOCK_REALTIME); - x ^= (unsigned) gettid(); - - srand(x); - srand_called = true; - - if (!pthread_atfork_registered) { - (void) pthread_atfork(NULL, NULL, clear_srand_initialization); - pthread_atfork_registered = true; - } -} - -/* INT_MAX gives us only 31 bits, so use 24 out of that. */ -#if RAND_MAX >= INT_MAX -assert_cc(RAND_MAX >= 16777215); -# define RAND_STEP 3 -#else -/* SHORT_INT_MAX or lower gives at most 15 bits, we just use 8 out of that. */ -assert_cc(RAND_MAX >= 255); -# define RAND_STEP 1 + memcpy(state.auxval, ULONG_TO_PTR(getauxval(AT_RANDOM)), sizeof(state.auxval)); #endif -void pseudo_random_bytes(void *p, size_t n) { - uint8_t *q; + while (n > 0) { + struct sha256_ctx ctx; - /* This returns pseudo-random data using libc's rand() function. You probably never want to call this - * directly, because why would you use this if you can get better stuff cheaply? Use random_bytes() - * instead, see below: it will fall back to this function if there's nothing better to get, but only - * then. */ - - initialize_srand(); - - for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) { - unsigned rr; - - rr = (unsigned) rand(); - -#if RAND_STEP >= 3 - if ((size_t) (q - (uint8_t*) p + 2) < n) - q[2] = rr >> 16; -#endif -#if RAND_STEP >= 2 - if ((size_t) (q - (uint8_t*) p + 1) < n) - q[1] = rr >> 8; -#endif - q[0] = rr; + sha256_init_ctx(&ctx); + sha256_process_bytes(&state, sizeof(state), &ctx); + if (n < SHA256_DIGEST_SIZE) { + uint8_t partial[SHA256_DIGEST_SIZE]; + sha256_finish_ctx(&ctx, partial); + memcpy(p, partial, n); + break; + } + sha256_finish_ctx(&ctx, p); + p = (uint8_t *) p + SHA256_DIGEST_SIZE; + n -= SHA256_DIGEST_SIZE; + ++state.block_id; } } void random_bytes(void *p, size_t n) { + static bool have_getrandom = true, have_grndinsecure = true; + _cleanup_close_ int fd = -1; - /* This returns high quality randomness if we can get it cheaply. If we can't because for some reason - * it is not available we'll try some crappy fallbacks. - * - * What this function will do: - * - * • Use getrandom(GRND_INSECURE) or /dev/urandom, to return high-quality random values if - * they are cheaply available, or less high-quality random values if they are not. - * - * • This function will return pseudo-random data, generated via libc rand() if nothing - * better is available. - * - * • This function will work fine in early boot - * - * • This function will always succeed - * - * What this function won't do: - * - * • This function will never fail: it will give you randomness no matter what. It might not - * be high quality, but it will return some, possibly generated via libc's rand() call. - * - * • This function will never block: if the only way to get good randomness is a blocking, - * synchronous getrandom() we'll instead provide you with pseudo-random data. - * - * This function is hence great for things like seeding hash tables, generating random numeric UNIX - * user IDs (that are checked for collisions before use) and such. - * - * This function is hence not useful for generating UUIDs or cryptographic key material. - */ - - if (genuine_random_bytes(p, n, 0) >= 0) + if (n == 0) return; - /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */ - pseudo_random_bytes(p, n); + for (;;) { + ssize_t l; + + if (!have_getrandom) + break; + + l = getrandom(p, n, have_grndinsecure ? GRND_INSECURE : GRND_NONBLOCK); + if (l > 0) { + if ((size_t) l == n) + return; /* Done reading, success. */ + p = (uint8_t *) p + l; + n -= l; + continue; /* Interrupted by a signal; keep going. */ + } else if (l == 0) + break; /* Weird, so fallback to /dev/urandom. */ + else if (ERRNO_IS_NOT_SUPPORTED(errno)) { + have_getrandom = false; + break; /* No syscall, so fallback to /dev/urandom. */ + } else if (errno == EINVAL && have_grndinsecure) { + have_grndinsecure = false; + continue; /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */ + } else if (errno == EAGAIN && !have_grndinsecure) + break; /* Will block, but no GRND_INSECURE, so fallback to /dev/urandom. */ + + break; /* Unexpected, so just give up and fallback to /dev/urandom. */ + } + + fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd >= 0 && loop_read_exact(fd, p, n, false) == 0) + return; + + /* This is a terrible fallback. Oh well. */ + fallback_random_bytes(p, n); +} + +int crypto_random_bytes(void *p, size_t n) { + static bool have_getrandom = true, seen_initialized = false; + _cleanup_close_ int fd = -1; + + if (n == 0) + return 0; + + for (;;) { + ssize_t l; + + if (!have_getrandom) + break; + + l = getrandom(p, n, 0); + if (l > 0) { + if ((size_t) l == n) + return 0; /* Done reading, success. */ + p = (uint8_t *) p + l; + n -= l; + continue; /* Interrupted by a signal; keep going. */ + } else if (l == 0) + return -EIO; /* Weird, should never happen. */ + else if (ERRNO_IS_NOT_SUPPORTED(errno)) { + have_getrandom = false; + break; /* No syscall, so fallback to /dev/urandom. */ + } + return -errno; + } + + if (!seen_initialized) { + _cleanup_close_ int ready_fd = -1; + int r; + + ready_fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (ready_fd < 0) + return -errno; + r = fd_wait_for_event(ready_fd, POLLIN, USEC_INFINITY); + if (r < 0) + return r; + seen_initialized = true; + } + + fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + return loop_read_exact(fd, p, n, false); } size_t random_pool_size(void) { diff --git a/src/basic/random-util.h b/src/basic/random-util.h index ccee32792f..2d99807272 100644 --- a/src/basic/random-util.h +++ b/src/basic/random-util.h @@ -5,15 +5,8 @@ #include #include -typedef enum RandomFlags { - RANDOM_BLOCK = 1 << 0, /* Rather block than return crap randomness (only if the kernel supports that) */ -} RandomFlags; - -int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */ -void pseudo_random_bytes(void *p, size_t n); /* returns only pseudo-randommess (but possibly seeded from something better) */ -void random_bytes(void *p, size_t n); /* returns genuine randomness if cheaply available, and pseudo randomness if not. */ - -void initialize_srand(void); +void random_bytes(void *p, size_t n); /* Returns random bytes suitable for most uses, but may be insecure sometimes. */ +int crypto_random_bytes(void *p, size_t n); /* Returns secure random bytes after waiting for the RNG to initialize. */ static inline uint64_t random_u64(void) { uint64_t u; diff --git a/src/basic/recovery-key.c b/src/basic/recovery-key.c index cad639a023..6a2f4d0f98 100644 --- a/src/basic/recovery-key.c +++ b/src/basic/recovery-key.c @@ -83,7 +83,7 @@ int make_recovery_key(char **ret) { if (!key) return -ENOMEM; - r = genuine_random_bytes(key, RECOVERY_KEY_MODHEX_RAW_LENGTH, RANDOM_BLOCK); + r = crypto_random_bytes(key, RECOVERY_KEY_MODHEX_RAW_LENGTH); if (r < 0) return r; diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index cebb479e75..814e068158 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -1805,7 +1805,7 @@ static int install_random_seed(const char *esp) { if (!buffer) return log_oom(); - r = genuine_random_bytes(buffer, sz, RANDOM_BLOCK); + r = crypto_random_bytes(buffer, sz); if (r < 0) return log_error_errno(r, "Failed to acquire random seed: %m"); @@ -1892,7 +1892,7 @@ static int install_random_seed(const char *esp) { log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sz); } - r = genuine_random_bytes(buffer, sz, RANDOM_BLOCK); + r = crypto_random_bytes(buffer, sz); if (r < 0) return log_error_errno(r, "Failed to acquire random seed: %m"); diff --git a/src/cryptenroll/cryptenroll-pkcs11.c b/src/cryptenroll/cryptenroll-pkcs11.c index eb098ce6f0..9f07a2e01d 100644 --- a/src/cryptenroll/cryptenroll-pkcs11.c +++ b/src/cryptenroll/cryptenroll-pkcs11.c @@ -50,7 +50,7 @@ int enroll_pkcs11( if (!decrypted_key) return log_oom(); - r = genuine_random_bytes(decrypted_key, decrypted_key_size, RANDOM_BLOCK); + r = crypto_random_bytes(decrypted_key, decrypted_key_size); if (r < 0) return log_error_errno(r, "Failed to generate random key: %m"); diff --git a/src/home/homectl-pkcs11.c b/src/home/homectl-pkcs11.c index c146c62e58..69c9d97aca 100644 --- a/src/home/homectl-pkcs11.c +++ b/src/home/homectl-pkcs11.c @@ -184,7 +184,7 @@ int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) { if (!decrypted_key) return log_oom(); - r = genuine_random_bytes(decrypted_key, decrypted_key_size, RANDOM_BLOCK); + r = crypto_random_bytes(decrypted_key, decrypted_key_size); if (r < 0) return log_error_errno(r, "Failed to generate random key: %m"); diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c index afa706a1bf..5106961f38 100644 --- a/src/home/homework-fscrypt.c +++ b/src/home/homework-fscrypt.c @@ -409,7 +409,7 @@ static int fscrypt_slot_set( const EVP_CIPHER *cc; size_t encrypted_size; - r = genuine_random_bytes(salt, sizeof(salt), RANDOM_BLOCK); + r = crypto_random_bytes(salt, sizeof(salt)); if (r < 0) return log_error_errno(r, "Failed to generate salt: %m"); @@ -540,7 +540,7 @@ int home_create_fscrypt( if (!volume_key) return log_oom(); - r = genuine_random_bytes(volume_key, volume_key_size, RANDOM_BLOCK); + r = crypto_random_bytes(volume_key, volume_key_size); if (r < 0) return log_error_errno(r, "Failed to acquire volume key: %m"); diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index 17c2f4f4e2..6541cb7ec9 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -949,7 +949,7 @@ static int format_luks_token_text( if (!iv) return log_oom(); - r = genuine_random_bytes(iv, iv_size, RANDOM_BLOCK); + r = crypto_random_bytes(iv, iv_size); if (r < 0) return log_error_errno(r, "Failed to generate IV: %m"); } @@ -1738,7 +1738,7 @@ static int luks_format( if (!volume_key) return log_oom(); - r = genuine_random_bytes(volume_key, volume_key_size, RANDOM_BLOCK); + r = crypto_random_bytes(volume_key, volume_key_size); if (r < 0) return log_error_errno(r, "Failed to generate volume key: %m"); diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index cff34fd585..046b48184a 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -1909,7 +1909,7 @@ static int setup_keys(void) { state = alloca_safe(state_size); log_info("Generating seed..."); - r = genuine_random_bytes(seed, seed_size, RANDOM_BLOCK); + r = crypto_random_bytes(seed, seed_size); if (r < 0) return log_error_errno(r, "Failed to acquire random seed: %m"); diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index 09c3401ca1..b81cd6ca8b 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -272,13 +272,10 @@ _public_ int sd_id128_get_invocation(sd_id128_t *ret) { _public_ int sd_id128_randomize(sd_id128_t *ret) { sd_id128_t t; - int r; assert_return(ret, -EINVAL); - r = genuine_random_bytes(&t, sizeof(t), 0); - if (r < 0) - return r; + random_bytes(&t, sizeof(t)); /* Turn this into a valid v4 UUID, to be nice. Note that we * only guarantee this for newly generated UUIDs, not for diff --git a/src/partition/repart.c b/src/partition/repart.c index 051242e836..a164dfefba 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -2594,7 +2594,7 @@ static int partition_encrypt( if (!volume_key) return log_oom(); - r = genuine_random_bytes(volume_key, volume_key_size, RANDOM_BLOCK); + r = crypto_random_bytes(volume_key, volume_key_size); if (r < 0) return log_error_errno(r, "Failed to generate volume key: %m"); diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index 3c89f527b4..72ec992121 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -158,7 +158,7 @@ static int make_credential_host_secret( .machine_id = machine_id, }; - r = genuine_random_bytes(buf.data, sizeof(buf.data), RANDOM_BLOCK); + r = crypto_random_bytes(buf.data, sizeof(buf.data)); if (r < 0) goto finish; @@ -642,7 +642,7 @@ int encrypt_credential_and_warn( if (!iv) return log_oom(); - r = genuine_random_bytes(iv, ivsz, RANDOM_BLOCK); + r = crypto_random_bytes(iv, ivsz); if (r < 0) return log_error_errno(r, "Failed to acquired randomized IV: %m"); } diff --git a/src/shared/libcrypt-util.c b/src/shared/libcrypt-util.c index 0d72032f53..81e6f1754c 100644 --- a/src/shared/libcrypt-util.c +++ b/src/shared/libcrypt-util.c @@ -76,7 +76,7 @@ int make_salt(char **ret) { log_debug("Generating fallback salt for hash prefix: $6$"); /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */ - r = genuine_random_bytes(raw, sizeof(raw), RANDOM_BLOCK); + r = crypto_random_bytes(raw, sizeof(raw)); if (r < 0) return r; diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c index e70a8c863a..796dc4079e 100644 --- a/src/shared/libfido2-util.c +++ b/src/shared/libfido2-util.c @@ -596,7 +596,7 @@ int fido2_generate_hmac_hash( if (!salt) return log_oom(); - r = genuine_random_bytes(salt, FIDO2_SALT_SIZE, RANDOM_BLOCK); + r = crypto_random_bytes(salt, FIDO2_SALT_SIZE); if (r < 0) return log_error_errno(r, "Failed to generate salt: %m"); diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 84120000aa..abcedde8a8 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -930,7 +930,7 @@ int tpm2_seal( log_debug("Generating secret key data."); - r = genuine_random_bytes(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size, RANDOM_BLOCK); + r = crypto_random_bytes(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size); if (r < 0) { log_error_errno(r, "Failed to generate secret key: %m"); goto finish; diff --git a/src/test/test-alloc-util.c b/src/test/test-alloc-util.c index f2064cf2ed..df6139005f 100644 --- a/src/test/test-alloc-util.c +++ b/src/test/test-alloc-util.c @@ -145,7 +145,7 @@ TEST(auto_erase_memory) { * end of the allocation, since malloc() enforces alignment */ assert_se(p2 = new(uint8_t, 4703)); - assert_se(genuine_random_bytes(p1, 4703, RANDOM_BLOCK) == 0); + assert_se(crypto_random_bytes(p1, 4703) == 0); /* before we exit the scope, do something with this data, so that the compiler won't optimize this away */ memcpy(p2, p1, 4703); diff --git a/src/test/test-firewall-util.c b/src/test/test-firewall-util.c index f22bc3c5bc..3ca3108e36 100644 --- a/src/test/test-firewall-util.c +++ b/src/test/test-firewall-util.c @@ -25,7 +25,7 @@ static void test_v6(FirewallContext *ctx) { assert_se(in_addr_from_string(AF_INET6, "1c3::c01d", &u2) >= 0); prefixlen = random_u64_range(128 + 1 - 8) + 8; - pseudo_random_bytes(&u3, sizeof(u3)); + random_bytes(&u3, sizeof(u3)); assert_se(fw_add_masquerade(&ctx, true, AF_INET6, &u1, 128) >= 0); assert_se(fw_add_masquerade(&ctx, false, AF_INET6, &u1, 128) >= 0); diff --git a/src/test/test-random-util.c b/src/test/test-random-util.c index 8cd457d57d..3128f669b7 100644 --- a/src/test/test-random-util.c +++ b/src/test/test-random-util.c @@ -9,13 +9,11 @@ #include "terminal-util.h" #include "tests.h" -static void test_genuine_random_bytes_one(RandomFlags flags) { +TEST(random_bytes) { uint8_t buf[16] = {}; - log_info("/* %s(%d) */", __func__, flags); - for (size_t i = 1; i < sizeof buf; i++) { - assert_se(genuine_random_bytes(buf, i, flags) == 0); + random_bytes(buf, i); if (i + 1 < sizeof buf) assert_se(buf[i] == 0); @@ -23,16 +21,11 @@ static void test_genuine_random_bytes_one(RandomFlags flags) { } } -TEST(genuine_random_bytes) { - test_genuine_random_bytes_one(0); - test_genuine_random_bytes_one(RANDOM_BLOCK); -} - -TEST(pseudo_random_bytes) { +TEST(crypto_random_bytes) { uint8_t buf[16] = {}; for (size_t i = 1; i < sizeof buf; i++) { - pseudo_random_bytes(buf, i); + assert_se(crypto_random_bytes(buf, i) == 0); if (i + 1 < sizeof buf) assert_se(buf[i] == 0); diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 28c0e633a3..8fdb48f315 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -623,10 +623,7 @@ static int link_generate_new_hw_addr(Link *link, struct hw_addr_data *ret) { /* We require genuine randomness here, since we want to make sure we won't collide with other * systems booting up at the very same time. */ for (;;) { - r = genuine_random_bytes(p, len, 0); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to acquire random data to generate MAC address: %m"); - + random_bytes(p, len); if (hw_addr_is_valid(link, &hw_addr)) break; } diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c index bde2811dd7..cfc13feced 100644 --- a/src/udev/scsi_id/scsi_serial.c +++ b/src/udev/scsi_id/scsi_serial.c @@ -789,7 +789,6 @@ int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname, int retval; memzero(dev_scsi->serial, len); - initialize_srand(); for (cnt = 20; cnt > 0; cnt--) { struct timespec duration; @@ -797,7 +796,7 @@ int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname, if (fd >= 0 || errno != EBUSY) break; duration.tv_sec = 0; - duration.tv_nsec = (200 * 1000 * 1000) + (rand() % 100 * 1000 * 1000); + duration.tv_nsec = (200 * 1000 * 1000) + (random_u32() % 100 * 1000 * 1000); nanosleep(&duration, NULL); } if (fd < 0)