blob: b60622e84f53c190069d953e8395d40e3b923aa5 [file] [log] [blame]
Hongwei Zhangcb66a9e2019-06-05 16:37:19 -04001
2/*
3* File: EINTR_wrappers.c
4*
5* This file implements the wrapper functions for some of the System APIs
6*
7* Copyright (C) <2019> <American Megatrends International LLC>
8*
9*/
10
11#include "EINTR_wrappers.h"
12#if defined(__linux__)
13#include <sys/msg.h>
14#include <sys/file.h>
15#endif
16#include <errno.h>
17#include <unistd.h>
18
19static const int OneSecondasNS = 1000000000;
20
21#ifndef bool
22typedef int bool;
23#endif
24
25#ifndef TRUE
26#define TRUE (1)
27#endif
28
29#ifndef FALSE
30#define FALSE (0)
31#endif
32
33typedef struct
34{
35 bool OnePoll;
36 struct timespec EndTime, Timeout;
37} SIGWRAP_TIMEOUT;
38
39static void sigwrap_InitTimeout(SIGWRAP_TIMEOUT *pDst, const struct timespec *timeout)
40{
41 pDst->Timeout = *timeout;
42
43 if ((timeout->tv_sec == 0) && (timeout->tv_nsec == 0)) // If both value are zero than only a single poll is requested!
44 {
45 pDst->OnePoll = 1;
46 return;
47 }
48
49 pDst->OnePoll = 0;
50
51 struct timespec Now;
52
53 (void)clock_gettime(CLOCK_MONOTONIC_RAW, &Now); // CLOCK_MONOTONIC_RAW is not affected by NTP etc.
54
55 pDst->EndTime.tv_sec = Now.tv_sec + pDst->Timeout.tv_sec; // Check necessary in 2038 due to signed integer variables
56 pDst->EndTime.tv_nsec = Now.tv_nsec + pDst->Timeout.tv_nsec;
57
58 if (pDst->EndTime.tv_nsec >= OneSecondasNS)
59 {
60 pDst->EndTime.tv_sec += (pDst->EndTime.tv_nsec / OneSecondasNS);
61 pDst->EndTime.tv_nsec = (pDst->EndTime.tv_nsec % OneSecondasNS);
62 }
63}
64
65
66static bool sigwrap_CheckTimeout(SIGWRAP_TIMEOUT *pTo)
67{
68 if (pTo->OnePoll == TRUE) // Make sure, that in the case that a single poll is requested at least one call is not terminated with EINTR
69 return FALSE;
70
71 struct timespec Now;
72
73 (void)clock_gettime(CLOCK_MONOTONIC_RAW, &Now);
74
75 if (Now.tv_sec > pTo->EndTime.tv_sec) // Can become a problem already in 2038 due to signed integer variables
76 return TRUE;
77
78 pTo->Timeout.tv_nsec = pTo->EndTime.tv_nsec - Now.tv_nsec;
79 pTo->Timeout.tv_sec = pTo->EndTime.tv_sec - Now.tv_sec;
80
81 if (pTo->Timeout.tv_sec == 0)
82 {
83 if (pTo->Timeout.tv_nsec <= 0)
84 return TRUE;
85 }
86 else if (pTo->Timeout.tv_nsec < 0)
87 {
88 pTo->Timeout.tv_nsec += OneSecondasNS;
89 pTo->Timeout.tv_sec--;
90 }
91
92 return FALSE;
93}
94
95
96
97int sigwrap_semop(int semid, struct sembuf *sops, size_t nsops)
98{
99 while (1)
100 {
101 if (semop(semid, sops, nsops) == 0)
102 return 0;
103
104 if (errno != EINTR)
105 return -1;
106 }
107}
108
109#if 0
110int sigwrap_semtimedop(int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout)
111{
112 SIGWRAP_TIMEOUT To;
113
114 if (timeout == NULL)
115 return (sigwrap_semop(semid, sops, nsops));
116
117 sigwrap_InitTimeout(&To, timeout);
118
119 while (1)
120 {
121 if (semtimedop(semid, sops, nsops, &To.Timeout) == 0)
122 return 0;
123
124 if (errno != EINTR)
125 return -1;
126
127 if (sigwrap_CheckTimeout(&To))
128 {
129 errno = EAGAIN;
130 return -1;
131 }
132 }
133}
134#endif
135
136int sigwrap_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
137{
138 SIGWRAP_TIMEOUT To;
139
140 if (timeout != -1)
141 {
142 struct timespec Timeout;
143
144 Timeout.tv_sec = timeout / 1000;
145 Timeout.tv_nsec = (timeout % 1000) * 1000000; // Convert msec to nsec
146
147 sigwrap_InitTimeout(&To, &Timeout);
148 }
149
150 while (1)
151 {
152 int Result = epoll_wait(epfd, events, maxevents, timeout);
153
154 if (Result != -1)
155 return Result;
156
157 if (errno != EINTR)
158 return Result;
159
160 if (timeout == -1)
161 continue;
162
163 if (sigwrap_CheckTimeout(&To))
164 return 0;
165
166 timeout = To.Timeout.tv_sec * 1000 + To.Timeout.tv_nsec / 1000000;
167 }
168}
169
170
171int sigwrap_epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask)
172{
173 SIGWRAP_TIMEOUT To;
174
175 if (timeout != -1)
176 {
177 struct timespec Timeout;
178
179 Timeout.tv_sec = timeout / 1000;
180 Timeout.tv_nsec = (timeout % 1000) * 1000000; // Convert msec to nsec
181
182 sigwrap_InitTimeout(&To, &Timeout);
183 }
184
185 while (1)
186 {
187 int Result = epoll_pwait(epfd, events, maxevents, timeout, sigmask);
188
189 if (Result != -1)
190 return Result;
191
192 if (errno != EINTR)
193 return Result;
194
195 if (timeout == -1)
196 continue;
197
198 if (sigwrap_CheckTimeout(&To))
199 return 0;
200
201 timeout = To.Timeout.tv_sec * 1000 + To.Timeout.tv_nsec / 1000000;
202 }
203}
204
205
206int sigwrap_sigwaitinfo(const sigset_t *set, siginfo_t *info)
207{
208 while (1)
209 {
210 int Result = sigwaitinfo(set, info);
211
212 if (Result != -1)
213 return Result;
214
215 if (errno != EINTR)
216 return Result;
217 }
218}
219
220
221int sigwrap_sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout)
222{
223 SIGWRAP_TIMEOUT To;
224
225 sigwrap_InitTimeout(&To, timeout);
226
227 while (1)
228 {
229 int Result = sigtimedwait(set, info, &To.Timeout);
230
231 if (Result != -1)
232 return Result;
233
234 if (errno != EINTR)
235 return Result;
236
237 if (sigwrap_CheckTimeout(&To))
238 return 0;
239 }
240}
241
242
243int sigwrap_nanosleep(const struct timespec *req, struct timespec *rem)
244{
245 struct timespec Wait, Remain;
246
247 if (!rem)
248 rem = &Remain;
249
250 Wait = *req;
251
252 while (1)
253 {
254 if (nanosleep(&Wait, rem) == 0)
255 return 0;
256
257 if (errno != EINTR)
258 return -1;
259
260 Wait = *rem;
261 }
262}
263
264
265int sigwrap_clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain)
266{
267 struct timespec Wait, Remain;
268
269 if (!remain)
270 remain = &Remain;
271
272 Wait = *request;
273
274 while (1)
275 {
276 int Result = clock_nanosleep(clock_id, flags, &Wait, remain);
277
278 if (Result == 0)
279 return Result;
280
281 if (Result != EINTR)
282 return Result;
283
284 if (flags != TIMER_ABSTIME)
285 Wait = *remain;
286 }
287}
288
289
290int sigwrap_usleep(useconds_t usec)
291{
292 SIGWRAP_TIMEOUT To;
293
294 struct timespec Timeout;
295
296 Timeout.tv_sec = usec / 1000000;
297 Timeout.tv_nsec = (usec % 1000000) * 1000;
298
299 sigwrap_InitTimeout(&To, &Timeout);
300
301 while (1)
302 {
303 if (usleep(usec) == 0)
304 return 0;
305
306 if (errno != EINTR)
307 return -1;
308
309 if (sigwrap_CheckTimeout(&To))
310 return 0;
311
312 usec = To.Timeout.tv_sec * 1000000 + To.Timeout.tv_nsec / 1000;
313 }
314}
315
316
317int sigwrap_poll(struct pollfd *fds, nfds_t nfds, int timeout)
318{
319 SIGWRAP_TIMEOUT To;
320
321 if (timeout > 0)
322 {
323 struct timespec Timeout;
324
325 Timeout.tv_sec = timeout / 1000;
326 Timeout.tv_nsec = (timeout % 1000) * 1000000;
327
328 sigwrap_InitTimeout(&To, &Timeout);
329 }
330
331 while (1)
332 {
333 int Result = poll(fds, nfds, timeout);
334
335 if (Result != -1)
336 return Result;
337
338 if (errno != EINTR)
339 return Result;
340
341 if (timeout < 0) // Specifying a negative value in timeout means an infinite/no timeout.
342 continue;
343 else if (timeout == 0)
344 continue; // We want to make sure that at least one check was not aborted with EINTR
345
346 if (sigwrap_CheckTimeout(&To))
347 return 0;
348
349 timeout = To.Timeout.tv_sec * 1000 + To.Timeout.tv_nsec / 1000000;
350 }
351}
352
353#if 0
354int sigwrap_ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *tmo_p, const sigset_t *sigmask)
355{
356 SIGWRAP_TIMEOUT To;
357
358 if (tmo_p != NULL)
359 {
360 sigwrap_InitTimeout(&To, tmo_p);
361 tmo_p = &To.Timeout;
362 }
363
364 while (1)
365 {
366 int Result = ppoll(fds, nfds, tmo_p, sigmask);
367
368 if (Result != -1)
369 return Result;
370
371 if (errno != EINTR)
372 return Result;
373
374 if (tmo_p == NULL)
375 continue;
376
377 if (sigwrap_CheckTimeout(&To))
378 return 0;
379 }
380}
381#endif
382
383int sigwrap_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
384{
385 while (1)
386 {
387 int Result = select(nfds, readfds, writefds, exceptfds, timeout);
388
389 if (Result != -1)
390 return Result;
391
392 if (errno != EINTR)
393 return Result;
394 }
395}
396
397
398int sigwrap_pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout,
399 const sigset_t *sigmask)
400{
401 SIGWRAP_TIMEOUT To;
402
403 if (timeout != NULL)
404 {
405 sigwrap_InitTimeout(&To, timeout);
406 timeout = &To.Timeout;
407 }
408
409 while (1)
410 {
411 int Result = pselect(nfds, readfds, writefds, exceptfds, timeout, sigmask);
412
413 if (Result != -1)
414 return Result;
415
416 if (errno != EINTR)
417 return Result;
418
419 if (timeout == NULL)
420 continue;
421
422 if (sigwrap_CheckTimeout(&To))
423 return 0;
424 }
425}
426
427
428int sigwrap_msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
429{
430 while (1)
431 {
432 int Result = msgsnd(msqid, msgp, msgsz, msgflg);
433
434 if (Result != -1)
435 return Result;
436
437 if (errno != EINTR)
438 return Result;
439 }
440}
441
442
443ssize_t sigwrap_msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
444{
445 while (1)
446 {
447 ssize_t Result = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
448
449 if (Result != -1)
450 return Result;
451
452 if (errno != EINTR)
453 return Result;
454 }
455}
456
457
458int sigwrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
459{
460 while (1)
461 {
462 int Result = connect(sockfd, addr, addrlen);
463
464 if (Result != -1)
465 return Result;
466
467 if (errno != EINTR)
468 return Result;
469 }
470}
471
472
473ssize_t sigwrap_send(int sockfd, const void *buf, size_t len, int flags)
474{
475 while (1)
476 {
477 ssize_t Result = send(sockfd, buf, len, flags);
478
479 if (Result != -1)
480 return Result;
481
482 if (errno != EINTR)
483 return Result;
484 }
485}
486
487
488ssize_t sigwrap_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr,
489 socklen_t addrlen)
490{
491 while (1)
492 {
493 ssize_t Result = sendto(sockfd, buf, len, flags, dest_addr, addrlen);
494
495 if (Result != -1)
496 return Result;
497
498 if (errno != EINTR)
499 return Result;
500 }
501}
502
503
504ssize_t sigwrap_sendsendmsg(int sockfd, const struct msghdr *msg, int flags)
505{
506 while (1)
507 {
508 ssize_t Result = sendmsg(sockfd, msg, flags);
509
510 if (Result != -1)
511 return Result;
512
513 if (errno != EINTR)
514 return Result;
515 }
516}
517
518
519int sigwrap_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
520{
521 while (1)
522 {
523 int Result = accept(sockfd, addr, addrlen);
524
525 if (Result != -1)
526 return Result;
527
528 if (errno != EINTR)
529 return Result;
530 }
531}
532
533#if 0
534int sigwrap_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags)
535{
536 while (1)
537 {
538 int Result = accept4(sockfd, addr, addrlen, flags);
539
540 if (Result != -1)
541 return Result;
542
543 if (errno != EINTR)
544 return Result;
545 }
546}
547#endif
548
549// EINTR wrapper for the standard read() function. Can be used for sockets that are the to non-blocking mode.
550// The length of the returned data can be shorter than the requested one!
551
552ssize_t sigwrap_read(int fd, void *buf, size_t count)
553{
554 while (1)
555 {
556 ssize_t Result = read(fd, buf, count);
557
558 if (Result != -1)
559 return (Result);
560
561 if (errno != EINTR)
562 return (Result);
563 }
564}
565
566
567// EINTR wrapper for the standard read() function. Waits until ALL requested data is available. Use the non-blocking version (sigwrap_read)
568// for sockets that are set to non-blocking mode or when partial data is okay
569// Although the description for the read() function describes it differently, it seems possible that the original function may already return
570// even though partial data has already been read. This implementation makes sure that all requested data have been read.
571// See the comment in the signal description https://linux.die.net/man/7/signal
572//* read(2), readv(2), write(2), writev(2), and ioctl(2) calls on "slow" devices.
573//* A "slow" device is one where the I/O call may block for an indefinite time, for example, a terminal, pipe, or socket.
574//* (A disk is not a slow device according to this definition.) If an I/O call on a slow device has already transferred
575//* some data by the time it is interrupted by a signal handler, then the call will return a success status (normally, the number of bytes transferred).
576
577ssize_t sigwrap_blocking_read(int hFile, void *pData, size_t RdLen)
578{
579 ssize_t Transfered;
580 ssize_t Len = RdLen;
581
582 while ((Transfered = read(hFile, pData, Len)) != Len)
583 {
584 if (Transfered == 0) // EOF reached?
585 return 0;
586
587 if (Transfered != -1)
588 {
589 pData += Transfered;
590 Len -= Transfered;
591 continue;
592 }
593
594 if (errno != EINTR)
595 return -1;
596 }
597
598 return RdLen;
599}
600
601
602ssize_t sigwrap_readv(int fd, const struct iovec *iov, int iovcnt)
603{
604 while (1)
605 {
606 ssize_t Result = readv(fd, iov, iovcnt);
607
608 if (Result != -1)
609 return (Result);
610
611 if (errno != EINTR)
612 return (Result);
613 }
614}
615
616
617ssize_t sigwrap_recv(int sockfd, void *buf, size_t len, int flags)
618{
619 while (1)
620 {
621 ssize_t Result = recv(sockfd, buf, len, flags);
622
623 if (Result != -1)
624 return (Result);
625
626 if (errno != EINTR)
627 return (Result);
628 }
629}
630
631
632ssize_t sigwrap_recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)
633{
634 while (1)
635 {
636 ssize_t Result = recvfrom(sockfd, buf, len, flags, src_addr, addrlen);
637
638 if (Result != -1)
639 return (Result);
640
641 if (errno != EINTR)
642 return (Result);
643 }
644}
645
646
647ssize_t sigwrap_recvmsg(int sockfd, struct msghdr *msg, int flags)
648{
649 while (1)
650 {
651 ssize_t Result = recvmsg(sockfd, msg, flags);
652
653 if (Result != -1)
654 return (Result);
655
656 if (errno != EINTR)
657 return (Result);
658 }
659}
660
661
662// EINTR wrapper for the standard write() function. Can be used for sockets that are the to non-blocking mode.
663// The length of the effectively written data can be shorter than the length specified at the function call!
664
665ssize_t sigwrap_write(int fd, const void *buf, size_t count)
666{
667 while (1)
668 {
669 ssize_t Result = write(fd, buf, count);
670
671 if (Result != -1)
672 return (Result);
673
674 if (errno != EINTR)
675 return (Result);
676 }
677}
678
679// EINTR wrapper for the standard write() function. Waits until ALL data is written! Use the non-blocking version (sigwrap_write)
680// for sockets that are set to non-blocking mode, or when it is OK to write only partial data.
681// Although the description for the write() function describes it differently, it seems possible that the original function may already return
682// even though partial data has already been written. This implementation makes sure that all requested data have been written.
683// See the comment in the signal description https://linux.die.net/man/7/signal
684//* read(2), readv(2), write(2), writev(2), and ioctl(2) calls on "slow" devices.
685//* A "slow" device is one where the I/O call may block for an indefinite time, for example, a terminal, pipe, or socket.
686//* (A disk is not a slow device according to this definition.) If an I/O call on a slow device has already transferred
687//* some data by the time it is interrupted by a signal handler, then the call will return a success status (normally, the number of bytes transferred).
688
689ssize_t sigwrap_blocking_write(int hFile, const void *pData, ssize_t WrtLen)
690{
691 ssize_t Written;
692 ssize_t Len = WrtLen;
693
694 while ((Written = write(hFile, pData, Len)) != Len)
695 {
696 if (Written != -1)
697 {
698 pData += Written;
699 Len -= Written;
700 continue;
701 }
702
703 if (errno != EINTR)
704 return -1;
705 }
706
707 return WrtLen;
708}
709
710
711ssize_t sigwrap_writev(int fd, const struct iovec *iov, int iovcnt)
712{
713 while (1)
714 {
715 ssize_t Result = writev(fd, iov, iovcnt);
716
717 if (Result != -1)
718 return (Result);
719
720 if (errno != EINTR)
721 return (Result);
722 }
723}
724
725
726int sigwrap_close(int hFile)
727{
728 while (close(hFile) == -1)
729 {
730 if (errno != EINTR)
731 return -1;
732 }
733
734 return 0;
735}
736
737
738int sigwrap_open_mode(const char *pathname, int flags, mode_t mode)
739{
740 while (1)
741 {
742 int hFile = open(pathname, flags, mode);
743
744 if(hFile != -1)
745 return hFile;
746
747 if (errno != EINTR)
748 return hFile;
749 }
750}
751
752int sigwrap_open(const char *pathname, int flags)
753{
754 while (1)
755 {
756 int hFile = open(pathname, flags);
757
758 if(hFile != -1)
759 return hFile;
760
761 if (errno != EINTR)
762 return hFile;
763 }
764}
765
766
767pid_t sigwrap_wait(int *status)
768{
769 while(1)
770 {
771 pid_t Result = wait(status);
772
773 if(Result != -1)
774 return Result;
775
776 if(errno != EINTR)
777 return Result;
778 }
779}
780
781
782pid_t sigwrap_waitpid(pid_t pid, int *status, int options)
783{
784 while(1)
785 {
786 pid_t Result = waitpid(pid, status, options);
787
788 if(Result != -1)
789 return Result;
790
791 if(errno != EINTR)
792 return Result;
793 }
794}
795
796
797int sigwrap_waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options)
798{
799 while(1)
800 {
801 int Result = waitid(idtype, id, infop, options);
802
803 if(Result != -1)
804 return Result;
805
806 if(errno != EINTR)
807 return Result;
808 }
809}
810
811
812int sigwrap_flock(int fd, int operation)
813{
814 while(1)
815 {
816 int Result = flock(fd, operation);
817
818 if(Result != -1)
819 return Result;
820
821 if(errno != EINTR)
822 return Result;
823 }
824}
825
826