xref: /lighttpd1.4/src/fdevent.c (revision fbf42d79)
1 #include "first.h"
2 
3 #include "fdevent.h"
4 
5 #include <sys/types.h>
6 #include "sys-socket.h"
7 
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 
14 #ifdef _WIN32
15 #include <sys/stat.h>   /* _S_IREAD _S_IWRITE */
16 #include <io.h>
17 #include <share.h>      /* _SH_DENYRW */
18 #include <winsock2.h>
19 #endif
20 
21 #include "ck.h"
22 #define force_assert(x) ck_assert(x)
23 
24 #ifdef SOCK_CLOEXEC
25 static int use_sock_cloexec;
26 #endif
27 #ifdef SOCK_NONBLOCK
28 static int use_sock_nonblock;
29 #endif
30 
fdevent_socket_nb_cloexec_init(void)31 void fdevent_socket_nb_cloexec_init (void)
32 {
33       #ifdef SOCK_CLOEXEC
34 	/* Test if SOCK_CLOEXEC is supported by kernel.
35 	 * Linux kernels < 2.6.27 might return EINVAL if SOCK_CLOEXEC used
36 	 * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=529929
37 	 * http://www.linksysinfo.org/index.php?threads/lighttpd-no-longer-starts-toastman-1-28-0510-7.73132/
38 	 * Test if SOCK_NONBLOCK is ignored by kernel on sockets.
39 	 * (reported on Android running a custom ROM)
40 	 * https://redmine.lighttpd.net/issues/2883
41 	 */
42        #ifdef SOCK_NONBLOCK
43 	int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
44        #else
45 	int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
46        #endif
47 	if (fd >= 0) {
48 		int flags = fcntl(fd, F_GETFL, 0);
49               #ifdef SOCK_NONBLOCK
50 		use_sock_nonblock = (-1 != flags && (flags & O_NONBLOCK));
51               #endif
52 		use_sock_cloexec = 1;
53 		close(fd);
54 	}
55       #endif
56 }
57 
fdevent_setfd_cloexec(int fd)58 void fdevent_setfd_cloexec(int fd) {
59 #ifdef FD_CLOEXEC
60 	if (fd < 0) return;
61 	force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
62 #else
63 	UNUSED(fd);
64 #endif
65 }
66 
fdevent_clrfd_cloexec(int fd)67 void fdevent_clrfd_cloexec(int fd) {
68 #ifdef FD_CLOEXEC
69 	if (fd >= 0) force_assert(-1 != fcntl(fd, F_SETFD, 0));
70 #else
71 	UNUSED(fd);
72 #endif
73 }
74 
fdevent_fcntl_set_nb(int fd)75 int fdevent_fcntl_set_nb(int fd) {
76 #ifdef O_NONBLOCK
77 	return fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
78 #else
79 	UNUSED(fd);
80 	return 0;
81 #endif
82 }
83 
fdevent_fcntl_set_nb_cloexec(int fd)84 int fdevent_fcntl_set_nb_cloexec(int fd) {
85 	fdevent_setfd_cloexec(fd);
86 	return fdevent_fcntl_set_nb(fd);
87 }
88 
fdevent_fcntl_set_nb_cloexec_sock(int fd)89 int fdevent_fcntl_set_nb_cloexec_sock(int fd) {
90 #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
91 	if (use_sock_cloexec && use_sock_nonblock)
92 		return 0;
93 #endif
94 	return fdevent_fcntl_set_nb_cloexec(fd);
95 }
96 
fdevent_socket_cloexec(int domain,int type,int protocol)97 int fdevent_socket_cloexec(int domain, int type, int protocol) {
98 	int fd;
99 #ifdef SOCK_CLOEXEC
100 	if (use_sock_cloexec)
101 		return socket(domain, type | SOCK_CLOEXEC, protocol);
102 #endif
103 	if (-1 != (fd = socket(domain, type, protocol))) {
104 #ifdef FD_CLOEXEC
105 		force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
106 #endif
107 	}
108 	return fd;
109 }
110 
fdevent_socket_nb_cloexec(int domain,int type,int protocol)111 int fdevent_socket_nb_cloexec(int domain, int type, int protocol) {
112 	int fd;
113 #ifdef SOCK_CLOEXEC
114        #ifdef SOCK_NONBLOCK
115 	if (use_sock_cloexec && use_sock_nonblock)
116 		return socket(domain, type | SOCK_CLOEXEC | SOCK_NONBLOCK, protocol);
117        #else
118 	if (use_sock_cloexec) {
119 		fd = socket(domain, type | SOCK_CLOEXEC, protocol);
120 	      #ifdef O_NONBLOCK
121 		if (-1 != fd) force_assert(-1 != fcntl(fd,F_SETFL,O_NONBLOCK|O_RDWR));
122 	      #endif
123 		return fd;
124 	}
125        #endif
126 #endif
127 	if (-1 != (fd = socket(domain, type, protocol))) {
128 #ifdef FD_CLOEXEC
129 		force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
130 #endif
131 #ifdef O_NONBLOCK
132 		force_assert(-1 != fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR));
133 #endif
134 	}
135 	return fd;
136 }
137 
fdevent_dup_cloexec(int fd)138 int fdevent_dup_cloexec (int fd) {
139   #ifdef F_DUPFD_CLOEXEC
140     return fcntl(fd, F_DUPFD_CLOEXEC, 3);
141   #else
142     const int newfd = fcntl(fd, F_DUPFD, 3);
143     if (newfd >= 0) fdevent_setfd_cloexec(newfd);
144     return newfd;
145   #endif
146 }
147 
148 #ifndef O_BINARY
149 #define O_BINARY 0
150 #endif
151 #ifndef O_LARGEFILE
152 #define O_LARGEFILE 0
153 #endif
154 #ifndef O_NOCTTY
155 #define O_NOCTTY 0
156 #endif
157 #ifndef O_NOFOLLOW
158 #define O_NOFOLLOW 0
159 #endif
160 
161 /*(O_NOFOLLOW is not handled here)*/
162 /*(Note: O_NOFOLLOW affects only the final path segment, the target file,
163  * not any intermediate symlinks along the path)*/
164 
165 /* O_CLOEXEC handled further below, if defined) */
166 #ifdef O_NONBLOCK
167 #define FDEVENT_O_FLAGS \
168         (O_BINARY | O_LARGEFILE | O_NOCTTY | O_NONBLOCK)
169 #else
170 #define FDEVENT_O_FLAGS \
171         (O_BINARY | O_LARGEFILE | O_NOCTTY )
172 #endif
173 
fdevent_open_cloexec(const char * pathname,int symlinks,int flags,mode_t mode)174 int fdevent_open_cloexec(const char *pathname, int symlinks, int flags, mode_t mode) {
175 	if (!symlinks) flags |= O_NOFOLLOW;
176 #ifdef O_CLOEXEC
177 	return open(pathname, flags | O_CLOEXEC | FDEVENT_O_FLAGS, mode);
178 #else
179 	int fd = open(pathname, flags | FDEVENT_O_FLAGS, mode);
180 #ifdef FD_CLOEXEC
181 	if (fd != -1)
182 		force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
183 #endif
184 	return fd;
185 #endif
186 }
187 
188 
fdevent_open_devnull(void)189 int fdevent_open_devnull(void) {
190   #if defined(_WIN32)
191     return fdevent_open_cloexec("nul", 0, O_RDWR, 0);
192   #elif defined(__sun) /* /dev/null is a symlink on Illumos */
193     return fdevent_open_cloexec("/dev/null", 1, O_RDWR, 0);
194   #else
195     return fdevent_open_cloexec("/dev/null", 0, O_RDWR, 0);
196   #endif
197 }
198 
199 
fdevent_open_dirname(char * path,int symlinks)200 int fdevent_open_dirname(char *path, int symlinks) {
201     /*(handle special cases of no dirname or dirname is root directory)*/
202     char * const c = strrchr(path, '/');
203     const char * const dname = (NULL != c ? c == path ? "/" : path : ".");
204     int dfd;
205     int flags = O_RDONLY;
206   #ifdef O_DIRECTORY
207     flags |= O_DIRECTORY;
208   #endif
209     if (NULL != c) *c = '\0';
210     dfd = fdevent_open_cloexec(dname, symlinks, flags, 0);
211     if (NULL != c) *c = '/';
212     return dfd;
213 }
214 
215 
216 #ifdef _WIN32
217 #include <stdio.h>
218 #endif
219 
fdevent_pipe_cloexec(int * const fds,const unsigned int bufsz_hint)220 int fdevent_pipe_cloexec (int * const fds, const unsigned int bufsz_hint) {
221  #ifdef _WIN32
222     return _pipe(fds, bufsz_hint, _O_BINARY | _O_NOINHERIT);
223  #else
224   #ifdef HAVE_PIPE2
225     if (0 != pipe2(fds, O_CLOEXEC))
226   #endif
227     {
228         if (0 != pipe(fds)
229          #ifdef FD_CLOEXEC
230          || 0 != fcntl(fds[0], F_SETFD, FD_CLOEXEC)
231          || 0 != fcntl(fds[1], F_SETFD, FD_CLOEXEC)
232          #endif
233            )
234             return -1;
235     }
236   #ifdef F_SETPIPE_SZ
237     if (bufsz_hint > 65536)
238         if (0 != fcntl(fds[1], F_SETPIPE_SZ, bufsz_hint)) { } /*(ignore error)*/
239   #else
240     UNUSED(bufsz_hint);
241   #endif
242     return 0;
243  #endif
244 }
245 
246 
fdevent_mkostemp(char * path,int flags)247 int fdevent_mkostemp(char *path, int flags) {
248  #ifdef _WIN32
249     /* future: if _sopen_s() returns EEXIST, might reset template (path) with
250      * trailing "XXXXXX", and then loop to try again */
251     int fd;      /*(flags might have _O_APPEND)*/
252     return (0 == _mktemp_s(path, strlen(path)+1))
253         && (0 == _sopen_s(&fd, path, _O_CREAT  | _O_EXCL   | _O_TEMPORARY
254                                    | flags     | _O_BINARY | _O_NOINHERIT,
255                           _SH_DENYRW, _S_IREAD | _S_IWRITE))
256       ? fd
257       : -1;
258  #elif defined(HAVE_MKOSTEMP)
259     return mkostemp(path, O_CLOEXEC | flags);
260  #else
261   #ifdef __COVERITY__
262     /* POSIX-2008 requires mkstemp create file with 0600 perms */
263     umask(0600);
264   #endif
265     /* coverity[secure_temp : FALSE] */
266     const int fd = mkstemp(path);
267     if (fd < 0) return fd;
268 
269     if (flags && 0 != fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | flags)) {
270         /* (should not happen; fd is regular file) */
271         int errnum = errno;
272         close(fd);
273         errno = errnum;
274         return -1;
275     }
276 
277     fdevent_setfd_cloexec(fd);
278     return fd;
279  #endif
280 }
281 
282 
283 /* accept4() added in Linux x86 in kernel 2.6.28, but not in arm until 2.6.36
284  * https://lwn.net/Articles/789961/ */
285 #if defined(__linux__) \
286  && (defined(__arm__) || defined(__thumb__) || defined(__arm64__))
287 #ifdef __has_include
288 #if __has_include(<sys/syscall.h>)
289 #include <sys/syscall.h>
290 #endif
291 #endif
292 #ifndef SYS_accept4
293 #define accept4(a,b,c,d) ((errno = ENOTSUP), -1)
294 #endif
295 #endif
296 
fdevent_accept_listenfd(int listenfd,struct sockaddr * addr,size_t * addrlen)297 int fdevent_accept_listenfd(int listenfd, struct sockaddr *addr, size_t *addrlen) {
298 	int fd;
299 	socklen_t len = (socklen_t) *addrlen;
300 
301       #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
302        #if defined(__NetBSD__)
303 	const int sock_cloexec = 1;
304 	fd = paccept(listenfd, addr, &len, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK);
305        #else
306 	int sock_cloexec = use_sock_cloexec;
307 	if (sock_cloexec) {
308 		fd = accept4(listenfd, addr, &len, SOCK_CLOEXEC | SOCK_NONBLOCK);
309 		if (fd >= 0) {
310 			if (!use_sock_nonblock) {
311 				if (0 != fdevent_fcntl_set_nb(fd)) {
312 					close(fd);
313 					fd = -1;
314 				}
315 			}
316 		}
317 		else {
318 			switch (errno) {
319 			case ENOSYS:
320 			case ENOTSUP:
321 			case EPERM:
322 				fd = accept(listenfd, addr, &len);
323 				sock_cloexec = 0;
324 				break;
325 			default:
326 				break;
327 			}
328 		}
329 	}
330 	else {
331 		fd = accept(listenfd, addr, &len);
332 	}
333        #endif
334       #else
335 	const int sock_cloexec = 0;
336 	fd = accept(listenfd, addr, &len);
337       #endif
338 
339 	if (fd >= 0) {
340 		*addrlen = (size_t)len;
341 		if (!sock_cloexec && 0 != fdevent_fcntl_set_nb_cloexec(fd)) {
342 			close(fd);
343 			fd = -1;
344 		}
345 	}
346 	return fd;
347 }
348 
349 
350 #ifdef __APPLE__
351 #include <crt_externs.h>
352 #define environ (* _NSGetEnviron())
353 #else
354 extern char **environ;
355 #endif
fdevent_environ(void)356 char ** fdevent_environ (void) { return environ; }
357 
358 
359 #ifdef FD_CLOEXEC
fdevent_dup2_close_clrfd_cloexec(int oldfd,int newfd)360 static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd) {
361     if (oldfd >= 0) {
362         if (oldfd != newfd) {
363             force_assert(oldfd > STDERR_FILENO);
364             if (newfd != dup2(oldfd, newfd)) return -1;
365         }
366         else {
367             fdevent_clrfd_cloexec(newfd);
368         }
369     }
370     return newfd;
371 }
372 #else
fdevent_dup2_close_clrfd_cloexec(int oldfd,int newfd,int reuse)373 static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd, int reuse) {
374     if (oldfd >= 0) {
375         if (oldfd != newfd) {
376             force_assert(oldfd > STDERR_FILENO);
377             if (newfd != dup2(oldfd, newfd)) return -1;
378             if (!reuse) close(oldfd);
379         }
380     }
381     return newfd;
382 }
383 #endif
384 
385 
fdevent_set_stdin_stdout_stderr(int fdin,int fdout,int fderr)386 int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr) {
387   #ifdef FD_CLOEXEC
388     if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO))
389         return -1;
390     if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO))
391         return -1;
392     if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO))
393         return -1;
394   #else
395     if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO,
396                                                          fdin == fdout
397                                                          || fdin == fderr))
398         return -1;
399     if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO,
400                                                           fdout == fderr))
401         return -1;
402     if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO,
403                                                           0))
404         return -1;
405   #endif
406 
407     return 0;
408 }
409 
410 
411 #include <stdio.h>      /* perror() rename() */
412 #include <signal.h>     /* signal() */
413 
414 
fdevent_rename(const char * oldpath,const char * newpath)415 int fdevent_rename(const char *oldpath, const char *newpath) {
416     return rename(oldpath, newpath);
417 }
418 
419 
fdevent_fork_execve(const char * name,char * argv[],char * envp[],int fdin,int fdout,int fderr,int dfd)420 pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd) {
421  #ifdef HAVE_FORK
422 
423     pid_t pid = fork();
424     if (0 != pid) return pid; /* parent (pid > 0) or fork() error (-1 == pid) */
425 
426     /* child (0 == pid) */
427 
428     if (-1 != dfd) {
429         if (0 != fchdir(dfd))
430             _exit(errno);
431         close(dfd);
432     }
433 
434     if (0 != fdevent_set_stdin_stdout_stderr(fdin, fdout, fderr)) _exit(errno);
435   #ifndef FD_CLOEXEC
436     /*(might not be sufficient for open fds, but modern OS have FD_CLOEXEC)*/
437     for (int i = 3; i < 256; ++i) close(i);
438   #endif
439 
440     /* reset_signals which may have been ignored (SIG_IGN) */
441   #ifdef SIGTTOU
442     signal(SIGTTOU, SIG_DFL);
443   #endif
444   #ifdef SIGTTIN
445     signal(SIGTTIN, SIG_DFL);
446   #endif
447   #ifdef SIGTSTP
448     signal(SIGTSTP, SIG_DFL);
449   #endif
450     signal(SIGPIPE, SIG_DFL);
451     signal(SIGUSR1, SIG_DFL);
452 
453     execve(name, argv, envp ? envp : environ);
454 
455     int errnum = errno;
456     int argnum =
457       (0 == strcmp(argv[0], "/bin/sh") && argv[1] && 0 == strcmp(argv[1], "-c"))
458       ? 2
459       : 0;
460     perror(argv[argnum]);
461     _exit(errnum);
462 
463  #else
464 
465     UNUSED(name);
466     UNUSED(argv);
467     UNUSED(envp);
468     UNUSED(fdin);
469     UNUSED(fdout);
470     UNUSED(fderr);
471     UNUSED(dfd);
472     return (pid_t)-1;
473 
474  #endif
475 }
476 
477 
478 #ifdef HAVE_SYS_WAIT_H
479 #include <sys/wait.h>
480 #endif
481 
fdevent_waitpid(pid_t pid,int * const status,int nb)482 int fdevent_waitpid(pid_t pid, int * const status, int nb) {
483     const int flags = nb ? WNOHANG : 0;
484     pid_t rv;
485     do { rv = waitpid(pid, status, flags); } while (-1 == rv && errno == EINTR);
486     return rv;
487 }
488 
fdevent_waitpid_intr(pid_t pid,int * const status)489 int fdevent_waitpid_intr(pid_t pid, int * const status) {
490     return waitpid(pid, status, 0);
491 }
492 
493 
494 #ifndef MSG_DONTWAIT
495 #define MSG_DONTWAIT 0
496 #endif
497 #ifndef MSG_NOSIGNAL
498 #define MSG_NOSIGNAL 0
499 #endif
500 
501 
fdevent_socket_read_discard(int fd,char * buf,size_t sz,int family,int so_type)502 ssize_t fdevent_socket_read_discard (int fd, char *buf, size_t sz, int family, int so_type) {
503   #if defined(MSG_TRUNC) && defined(__linux__)
504     if ((family == AF_INET || family == AF_INET6) && so_type == SOCK_STREAM) {
505         ssize_t len = recv(fd, buf, sz, MSG_TRUNC|MSG_DONTWAIT|MSG_NOSIGNAL);
506         if (len >= 0 || errno != EINVAL) return len;
507     }
508   #else
509     UNUSED(family);
510     UNUSED(so_type);
511   #endif
512     return read(fd, buf, sz);
513 }
514 
515 
516 #include <sys/ioctl.h>
517 #ifdef HAVE_SYS_FILIO_H
518 #include <sys/filio.h>  /* FIONREAD (for illumos (OpenIndiana)) */
519 #endif
520 #ifdef _WIN32
521 #include <winsock2.h>
522 #endif
fdevent_ioctl_fionread(int fd,int fdfmt,int * toread)523 int fdevent_ioctl_fionread (int fd, int fdfmt, int *toread) {
524   #ifdef _WIN32
525     if (fdfmt != S_IFSOCK) { errno = ENOTSOCK; return -1; }
526     return ioctlsocket(fd, FIONREAD, toread);
527   #else
528    #ifdef __CYGWIN__
529     /*(cygwin supports FIONREAD on pipes, not sockets)*/
530     if (fdfmt != S_IFIFO) { errno = EOPNOTSUPP; return -1; }
531    #else
532     UNUSED(fdfmt);
533    #endif
534     return ioctl(fd, FIONREAD, toread);
535   #endif
536 }
537 
538 
fdevent_connect_status(int fd)539 int fdevent_connect_status(int fd) {
540     /* try to finish the connect() */
541     /*(should be called after connect() only when fd is writable (POLLOUT))*/
542     int opt;
543     socklen_t len = sizeof(opt);
544     return (0 == getsockopt(fd,SOL_SOCKET,SO_ERROR,&opt,&len)) ? opt : errno;
545 }
546 
547 
548 #include <netinet/tcp.h>
549 #if  defined(__FreeBSD__) || defined(__NetBSD__) \
550   || defined(__OpenBSD__) || defined(__DragonFly__)
551 #include <netinet/tcp_fsm.h>
552 #endif
553 #if defined(__APPLE__) && defined(__MACH__)
554 #include <TargetConditionals.h> /* TARGET_OS_IPHONE, TARGET_OS_MAC */
555 #if TARGET_OS_IPHONE            /* iOS, tvOS, or watchOS device */
556 /*#define TCPS_CLOSE_WAIT 5*/   /* ??? which header contains this, if any ??? */
557 #elif TARGET_OS_MAC             /* MacOS */
558 #include <netinet/tcp_fsm.h>
559 #endif
560 #endif
561 
562 /* fd must be TCP socket (AF_INET, AF_INET6), end-of-stream recv() 0 bytes */
fdevent_is_tcp_half_closed(int fd)563 int fdevent_is_tcp_half_closed(int fd) {
564   #ifdef TCP_CONNECTION_INFO     /* Darwin */
565     struct tcp_connection_info tcpi;
566     socklen_t tlen = sizeof(tcpi);
567     return (0 == getsockopt(fd, IPPROTO_TCP, TCP_CONNECTION_INFO, &tcpi, &tlen)
568             && tcpi.tcpi_state == TCPS_CLOSE_WAIT);
569   #elif defined(TCP_INFO) && defined(TCPS_CLOSE_WAIT)
570     /* FreeBSD, NetBSD, OpenBSD (not present in DragonFlyBSD) */
571     struct tcp_info tcpi;
572     socklen_t tlen = sizeof(tcpi);
573     return (0 == getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpi, &tlen)
574             && tcpi.tcpi_state == TCPS_CLOSE_WAIT);
575   #elif defined(TCP_INFO) && defined(__linux__)
576     /* Linux (TCP_CLOSE_WAIT is enum, so can not #ifdef TCP_CLOSE_WAIT) */
577     struct tcp_info tcpi;
578     socklen_t tlen = sizeof(tcpi);/*SOL_TCP == IPPROTO_TCP*/
579     return (0 == getsockopt(fd,     SOL_TCP, TCP_INFO, &tcpi, &tlen)
580             && tcpi.tcpi_state == TCP_CLOSE_WAIT);
581   #else
582     UNUSED(fd);
583     /*(0 != getpeername() error might indicate TCP RST, but success
584      * would not differentiate between half-close and full-close)*/
585     return 0; /* false (not half-closed) or TCP state unknown */
586   #endif
587 }
588 
589 
fdevent_set_tcp_nodelay(const int fd,const int opt)590 int fdevent_set_tcp_nodelay (const int fd, const int opt)
591 {
592     return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
593 }
594 
595 
fdevent_set_so_reuseaddr(const int fd,const int opt)596 int fdevent_set_so_reuseaddr (const int fd, const int opt)
597 {
598     return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
599 }
600 
601 
602 #include <sys/stat.h>
603 #include "ck.h"
604 #include "log.h"
605 __attribute_cold__ /*(convenience routine for use at config at startup)*/
606 char *
fdevent_load_file(const char * const fn,off_t * lim,log_error_st * errh,void * (malloc_fn)(size_t),void (free_fn)(void *))607 fdevent_load_file (const char * const fn, off_t *lim, log_error_st *errh, void *(malloc_fn)(size_t), void(free_fn)(void *))
608 {
609     int fd;
610     off_t sz = 0;
611     char *buf = NULL;
612     do {
613         fd = fdevent_open_cloexec(fn, 1, O_RDONLY, 0); /*(1: follows symlinks)*/
614         if (fd < 0) break;
615 
616         struct stat st;
617         if (0 != fstat(fd, &st)) break;
618         if ((sizeof(off_t) > sizeof(size_t) && st.st_size >= (off_t)~(size_t)0u)
619             || (*lim != 0 && st.st_size >= *lim)) {
620             errno = EOVERFLOW;
621             break;
622         }
623 
624         sz = st.st_size;
625         buf = malloc_fn((size_t)sz+1);/*+1 trailing '\0' for str funcs on data*/
626         if (NULL == buf) break;
627 
628         if (sz) {
629             ssize_t rd = 0;
630             off_t off = 0;
631             do {
632                 rd = read(fd, buf+off, (size_t)(sz-off));
633             } while (rd > 0 ? (off += rd) != sz : rd < 0 && errno == EINTR);
634             if (off != sz) { /*(file truncated?)*/
635                 if (rd >= 0) errno = EIO;
636                 break;
637             }
638         }
639 
640         buf[sz] = '\0';
641         *lim = sz;
642         close(fd);
643         return buf;
644     } while (0);
645     int errnum = errno;
646     if (errh)
647         log_perror(errh, __FILE__, __LINE__, "%s() %s", __func__, fn);
648     if (fd >= 0) close(fd);
649     if (buf) {
650         ck_memzero(buf, (size_t)sz);
651         free_fn(buf);
652     }
653     *lim = 0;
654     errno = errnum;
655     return NULL;
656 }
657 
658 
659 int
fdevent_load_file_bytes(char * const buf,const off_t sz,off_t off,const char * const fn,log_error_st * errh)660 fdevent_load_file_bytes (char * const buf, const off_t sz, off_t off, const char * const fn, log_error_st *errh)
661 {
662     int fd;
663     do {
664         fd = fdevent_open_cloexec(fn, 1, O_RDONLY, 0); /*(1: follows symlinks)*/
665         if (fd < 0) break;
666 
667         if (0 != off && (off_t)-1 == lseek(fd, off, SEEK_SET)) break;
668         off = 0;
669 
670         ssize_t rd = 0;
671         do {
672             rd = read(fd, buf+off, (size_t)(sz-off));
673         } while (rd > 0 ? (off += rd) != sz : rd < 0 && errno == EINTR);
674         if (off != sz) { /*(file truncated? or incorrect sz requested)*/
675             if (rd >= 0) errno = EIO;
676             break;
677         }
678 
679         close(fd);
680         return 0;
681     } while (0);
682     int errnum = errno;
683     if (errh)
684         log_perror(errh, __FILE__, __LINE__, "%s() %s", __func__, fn);
685     if (fd >= 0) close(fd);
686     ck_memzero(buf, (size_t)sz);
687     errno = errnum;
688     return -1;
689 }
690