1 #ifndef _BABELTRACE_COMPAT_SEND_H
2 #define _BABELTRACE_COMPAT_SEND_H
3 
4 /*
5  * babeltrace/compat/send.h
6  *
7  * Copyright (C) 2015  Michael Jeanson <mjeanson@efficios.com>
8  *               2015  Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  */
28 
29 /*
30  * This wrapper is used on platforms that have no way of ignoring SIGPIPE
31  * during a send().
32  */
33 
34 #ifndef MSG_NOSIGNAL
35 # ifdef SO_NOSIGPIPE
36 #   define MSG_NOSIGNAL SO_NOSIGPIPE
37 # endif
38 #endif
39 
40 #if defined(MSG_NOSIGNAL)
41 static inline
bt_send_nosigpipe(int fd,const void * buffer,size_t size)42 ssize_t bt_send_nosigpipe(int fd, const void *buffer, size_t size)
43 {
44 	return send(fd, buffer, size, MSG_NOSIGNAL);
45 }
46 #else
47 
48 #include <signal.h>
49 
50 static inline
bt_send_nosigpipe(int fd,const void * buffer,size_t size)51 ssize_t bt_send_nosigpipe(int fd, const void *buffer, size_t size)
52 {
53 	ssize_t sent;
54 	int saved_err;
55 	sigset_t sigpipe_set, pending_set, old_set;
56 	int sigpipe_was_pending;
57 
58 	/*
59 	 * Discard the SIGPIPE from send(), not disturbing any SIGPIPE
60 	 * that might be already pending. If a bogus SIGPIPE is sent to
61 	 * the entire process concurrently by a malicious user, it may
62 	 * be simply discarded.
63 	 */
64 	if (sigemptyset(&pending_set)) {
65 		return -1;
66 	}
67 	/*
68 	 * sigpending returns the mask of signals that are _both_
69 	 * blocked for the thread _and_ pending for either the thread or
70 	 * the entire process.
71 	 */
72 	if (sigpending(&pending_set)) {
73 		return -1;
74 	}
75 	sigpipe_was_pending = sigismember(&pending_set, SIGPIPE);
76 	/*
77 	 * If sigpipe was pending, it means it was already blocked, so
78 	 * no need to block it.
79 	 */
80 	if (!sigpipe_was_pending) {
81 		if (sigemptyset(&sigpipe_set)) {
82 			return -1;
83 		}
84 		if (sigaddset(&sigpipe_set, SIGPIPE)) {
85 			return -1;
86 		}
87 		if (pthread_sigmask(SIG_BLOCK, &sigpipe_set, &old_set)) {
88 			return -1;
89 		}
90 	}
91 
92 	/* Send and save errno. */
93 	sent = send(fd, buffer, size, 0);
94 	saved_err = errno;
95 
96 	if (sent == -1 && errno == EPIPE && !sigpipe_was_pending) {
97 		struct timespec timeout = { 0, 0 };
98 		int ret;
99 
100 		do {
101 			ret = sigtimedwait(&sigpipe_set, NULL,
102 				&timeout);
103 		} while (ret == -1 && errno == EINTR);
104 	}
105 	if (!sigpipe_was_pending) {
106 		if (pthread_sigmask(SIG_SETMASK, &old_set, NULL)) {
107 			return -1;
108 		}
109 	}
110 	/* Restore send() errno */
111 	errno = saved_err;
112 
113 	return sent;
114 }
115 #endif
116 
117 #endif /* _BABELTRACE_COMPAT_SEND_H */
118