Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2020 Yubico AB. All rights reserved. |
3 | | * Use of this source code is governed by a BSD-style |
4 | | * license that can be found in the LICENSE file. |
5 | | */ |
6 | | |
7 | | #include <sys/types.h> |
8 | | #include <sys/stat.h> |
9 | | |
10 | | #include <errno.h> |
11 | | #include <fcntl.h> |
12 | | #include <limits.h> |
13 | | #include <poll.h> |
14 | | #include <stdint.h> |
15 | | #include <stdio.h> |
16 | | #include <stdlib.h> |
17 | | #include <string.h> |
18 | | #include <time.h> |
19 | | #include <unistd.h> |
20 | | |
21 | | #include "fido.h" |
22 | | |
23 | | static void |
24 | | xstrerror(int errnum, char *buf, size_t len) |
25 | 0 | { |
26 | 0 | if (len < 1) |
27 | 0 | return; |
28 | 0 | |
29 | 0 | memset(buf, 0, len); |
30 | 0 |
|
31 | 0 | if (strerror_r(errnum, buf, len - 1) != 0) |
32 | 0 | snprintf(buf, len - 1, "error %d", errnum); |
33 | 0 | } |
34 | | |
35 | | static int |
36 | | timespec_to_ms(const struct timespec *ts, int upper_bound) |
37 | 0 | { |
38 | 0 | int64_t x; |
39 | 0 | int64_t y; |
40 | 0 |
|
41 | 0 | if (ts->tv_sec < 0 || (uint64_t)ts->tv_sec > INT64_MAX / 1000LL || |
42 | 0 | ts->tv_nsec < 0 || (uint64_t)ts->tv_nsec / 1000000LL > INT64_MAX) |
43 | 0 | return (upper_bound); |
44 | 0 | |
45 | 0 | x = ts->tv_sec * 1000LL; |
46 | 0 | y = ts->tv_nsec / 1000000LL; |
47 | 0 |
|
48 | 0 | if (INT64_MAX - x < y || x + y > upper_bound) |
49 | 0 | return (upper_bound); |
50 | 0 | |
51 | 0 | return (int)(x + y); |
52 | 0 | } |
53 | | |
54 | | int |
55 | | fido_hid_unix_open(const char *path) |
56 | 0 | { |
57 | 0 | char ebuf[128]; |
58 | 0 | int fd; |
59 | 0 | struct stat st; |
60 | 0 |
|
61 | 0 | if ((fd = open(path, O_RDWR)) == -1) { |
62 | 0 | if (errno != ENOENT && errno != ENXIO) { |
63 | 0 | xstrerror(errno, ebuf, sizeof(ebuf)); |
64 | 0 | fido_log_debug("%s: open %s: %s", __func__, path, ebuf); |
65 | 0 | } |
66 | 0 | return (-1); |
67 | 0 | } |
68 | 0 |
|
69 | 0 | if (fstat(fd, &st) == -1) { |
70 | 0 | xstrerror(errno, ebuf, sizeof(ebuf)); |
71 | 0 | fido_log_debug("%s: fstat %s: %s", __func__, path, ebuf); |
72 | 0 | close(fd); |
73 | 0 | return (-1); |
74 | 0 | } |
75 | 0 | |
76 | 0 | if (S_ISCHR(st.st_mode) == 0) { |
77 | 0 | fido_log_debug("%s: S_ISCHR %s", __func__, path); |
78 | 0 | close(fd); |
79 | 0 | return (-1); |
80 | 0 | } |
81 | 0 | |
82 | 0 | return (fd); |
83 | 0 | } |
84 | | |
85 | | int |
86 | | fido_hid_unix_wait(int fd, int ms) |
87 | 0 | { |
88 | 0 | char ebuf[128]; |
89 | 0 | struct timespec ts_start; |
90 | 0 | struct timespec ts_now; |
91 | 0 | struct timespec ts_delta; |
92 | 0 | struct pollfd pfd; |
93 | 0 | int ms_remain; |
94 | 0 | int r; |
95 | 0 |
|
96 | 0 | if (ms < 0) |
97 | 0 | return (0); |
98 | 0 | |
99 | 0 | memset(&pfd, 0, sizeof(pfd)); |
100 | 0 | pfd.events = POLLIN; |
101 | 0 | pfd.fd = fd; |
102 | 0 |
|
103 | 0 | if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) { |
104 | 0 | xstrerror(errno, ebuf, sizeof(ebuf)); |
105 | 0 | fido_log_debug("%s: clock_gettime: %s", __func__, ebuf); |
106 | 0 | return (-1); |
107 | 0 | } |
108 | 0 | |
109 | 0 | for (ms_remain = ms; ms_remain > 0;) { |
110 | 0 | if ((r = poll(&pfd, 1, ms_remain)) > 0) |
111 | 0 | return (0); |
112 | 0 | else if (r == 0) |
113 | 0 | break; |
114 | 0 | else if (errno != EINTR) { |
115 | 0 | xstrerror(errno, ebuf, sizeof(ebuf)); |
116 | 0 | fido_log_debug("%s: poll: %s", __func__, ebuf); |
117 | 0 | return (-1); |
118 | 0 | } |
119 | 0 | /* poll interrupted - subtract time already waited */ |
120 | 0 | if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) { |
121 | 0 | xstrerror(errno, ebuf, sizeof(ebuf)); |
122 | 0 | fido_log_debug("%s: clock_gettime: %s", __func__, ebuf); |
123 | 0 | return (-1); |
124 | 0 | } |
125 | 0 | timespecsub(&ts_now, &ts_start, &ts_delta); |
126 | 0 | ms_remain = ms - timespec_to_ms(&ts_delta, ms); |
127 | 0 | } |
128 | 0 |
|
129 | 0 | return (-1); |
130 | 0 | } |