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