1 /*****************************************************************************\
2  *  Copyright (C) 2001-2002 The Regents of the University of California.
3  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
4  *  Written by Chris Dunlap <cdunlap@llnl.gov>.
5  *  UCRL-CODE-2002-009.
6  *
7  *  This file is part of ConMan, a remote console management program.
8  *  For details, see <http://www.llnl.gov/linux/conman/>.
9  *
10  *  ConMan is free software; you can redistribute it and/or modify it under
11  *  the terms of the GNU General Public License as published by the Free
12  *  Software Foundation; either version 2 of the License, or (at your option)
13  *  any later version.
14  *
15  *  In addition, as a special exception, the copyright holders give permission
16  *  to link the code of portions of this program with the OpenSSL library under
17  *  certain conditions as described in each individual source file, and
18  *  distribute linked combinations including the two. You must obey the GNU
19  *  General Public License in all respects for all of the code used other than
20  *  OpenSSL. If you modify file(s) with this exception, you may extend this
21  *  exception to your version of the file(s), but you are not obligated to do
22  *  so. If you do not wish to do so, delete this exception statement from your
23  *  version.  If you delete this exception statement from all source files in
24  *  the program, then also delete it here.
25  *
26  *  ConMan is distributed in the hope that it will be useful, but WITHOUT ANY
27  *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
28  *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
29  *  details.
30  *
31  *  You should have received a copy of the GNU General Public License along
32  *  with ConMan; if not, write to the Free Software Foundation, Inc.,
33  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
34  *****************************************************************************
35  *  Refer to "fd.h" for documentation on public functions.
36 \*****************************************************************************/
37 
38 #include <assert.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <inttypes.h>
42 #include <poll.h>
43 #include <sys/socket.h>
44 #include <sys/types.h>
45 #include <unistd.h>
46 
47 #include "slurm/slurm_errno.h"
48 
49 #include "src/common/fd.h"
50 #include "src/common/log.h"
51 #include "src/common/macros.h"
52 #include "src/common/timers.h"
53 #include "src/common/xassert.h"
54 #include "src/common/xmalloc.h"
55 #include "src/common/xstring.h"
56 
57 /*
58  * Define slurm-specific aliases for use by plugins, see slurm_xlator.h
59  * for details.
60  */
61 strong_alias(fd_set_blocking,	slurm_fd_set_blocking);
62 strong_alias(fd_set_nonblocking,slurm_fd_set_nonblocking);
63 strong_alias(fd_get_socket_error, slurm_fd_get_socket_error);
64 strong_alias(send_fd_over_pipe, slurm_send_fd_over_pipe);
65 strong_alias(receive_fd_over_pipe, slurm_receive_fd_over_pipe);
66 
67 static int fd_get_lock(int fd, int cmd, int type);
68 static pid_t fd_test_lock(int fd, int type);
69 
70 
fd_set_close_on_exec(int fd)71 void fd_set_close_on_exec(int fd)
72 {
73 	xassert(fd >= 0);
74 
75 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
76 		error("fcntl(F_SETFD) failed: %m");
77 	return;
78 }
79 
fd_set_noclose_on_exec(int fd)80 void fd_set_noclose_on_exec(int fd)
81 {
82 	xassert(fd >= 0);
83 
84 	if (fcntl(fd, F_SETFD, 0) < 0)
85 		error("fcntl(F_SETFD) failed: %m");
86 	return;
87 }
88 
fd_set_nonblocking(int fd)89 void fd_set_nonblocking(int fd)
90 {
91 	int fval;
92 
93 	xassert(fd >= 0);
94 
95 	if ((fval = fcntl(fd, F_GETFL, 0)) < 0)
96 		error("fcntl(F_GETFL) failed: %m");
97 	if (fcntl(fd, F_SETFL, fval | O_NONBLOCK) < 0)
98 		error("fcntl(F_SETFL) failed: %m");
99 	return;
100 }
101 
fd_set_blocking(int fd)102 void fd_set_blocking(int fd)
103 {
104 	int fval;
105 
106 	xassert(fd >= 0);
107 
108 	if ((fval = fcntl(fd, F_GETFL, 0)) < 0)
109 		error("fcntl(F_GETFL) failed: %m");
110 	if (fcntl(fd, F_SETFL, fval & ~O_NONBLOCK) < 0)
111 		error("fcntl(F_SETFL) failed: %m");
112 	return;
113 }
114 
fd_get_readw_lock(int fd)115 int fd_get_readw_lock(int fd)
116 {
117 	return(fd_get_lock(fd, F_SETLKW, F_RDLCK));
118 }
119 
120 
fd_get_write_lock(int fd)121 int fd_get_write_lock(int fd)
122 {
123 	return(fd_get_lock(fd, F_SETLK, F_WRLCK));
124 }
125 
fd_release_lock(int fd)126 int fd_release_lock(int fd)
127 {
128 	return(fd_get_lock(fd, F_SETLK, F_UNLCK));
129 }
130 
131 
fd_is_read_lock_blocked(int fd)132 pid_t fd_is_read_lock_blocked(int fd)
133 {
134 	return(fd_test_lock(fd, F_RDLCK));
135 }
136 
fd_get_socket_error(int fd,int * err)137 int fd_get_socket_error(int fd, int *err)
138 {
139 	socklen_t errlen = sizeof(err);
140 
141 	xassert(fd >= 0);
142 
143 	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen))
144 		return errno;
145 	else
146 		return SLURM_SUCCESS;
147 }
148 
fd_get_lock(int fd,int cmd,int type)149 static int fd_get_lock(int fd, int cmd, int type)
150 {
151 	struct flock lock;
152 
153 	xassert(fd >= 0);
154 
155 	lock.l_type = type;
156 	lock.l_start = 0;
157 	lock.l_whence = SEEK_SET;
158 	lock.l_len = 0;
159 
160 	return(fcntl(fd, cmd, &lock));
161 }
162 
163 
fd_test_lock(int fd,int type)164 static pid_t fd_test_lock(int fd, int type)
165 {
166 	struct flock lock;
167 
168 	xassert(fd >= 0);
169 
170 	lock.l_type = type;
171 	lock.l_start = 0;
172 	lock.l_whence = SEEK_SET;
173 	lock.l_len = 0;
174 	lock.l_pid = 0;	/* avoid valgrind error */
175 
176 	if (fcntl(fd, F_GETLK, &lock) < 0)
177 		error("Unable to test for file lock: %m");
178 	if (lock.l_type == F_UNLCK)
179 		return(0);
180 	return(lock.l_pid);
181 }
182 
183 
184 /* Wait for a file descriptor to be readable (up to time_limit seconds).
185  * Return 0 when readable or -1 on error */
wait_fd_readable(int fd,int time_limit)186 extern int wait_fd_readable(int fd, int time_limit)
187 {
188 	struct pollfd ufd;
189 	time_t start;
190 	int rc, time_left;
191 
192 	start = time(NULL);
193 	time_left = time_limit;
194 	ufd.fd = fd;
195 	ufd.events = POLLIN;
196 	ufd.revents = 0;
197 	while (1) {
198 		rc = poll(&ufd, 1, time_left * 1000);
199 		if (rc > 0) {	/* activity on this fd */
200 			if (ufd.revents & POLLIN)
201 				return 0;
202 			else	/* Exception */
203 				return -1;
204 		} else if (rc == 0) {
205 			error("Timeout waiting for slurmstepd");
206 			return -1;
207 		} else if (errno != EINTR) {
208 			error("poll(): %m");
209 			return -1;
210 		} else {
211 			time_left = time_limit - (time(NULL) - start);
212 		}
213 	}
214 }
215 
216 /*
217  * fsync() then close() a file.
218  * Execute fsync() and close() multiple times if necessary and log failures
219  * RET 0 on success or -1 on error
220  */
fsync_and_close(int fd,const char * file_type)221 extern int fsync_and_close(int fd, const char *file_type)
222 {
223 	int rc = 0, retval, pos;
224 	DEF_TIMERS;
225 
226 	/*
227 	 * Slurm state save files are commonly stored on shared filesystems,
228 	 * so lets give fsync() three tries to sync the data to disk.
229 	 */
230 	START_TIMER;
231 	for (retval = 1, pos = 1; retval && pos < 4; pos++) {
232 		retval = fsync(fd);
233 		if (retval && (errno != EINTR)) {
234 			error("fsync() error writing %s state save file: %m",
235 			      file_type);
236 		}
237 	}
238 	END_TIMER2("fsync_and_close:fsync");
239 	if (retval)
240 		rc = retval;
241 
242 	START_TIMER;
243 	for (retval = 1, pos = 1; retval && pos < 4; pos++) {
244 		retval = close(fd);
245 		if (retval && (errno != EINTR)) {
246 			error("close () error on %s state save file: %m",
247 			      file_type);
248 		}
249 	}
250 	END_TIMER2("fsync_and_close:close");
251 	if (retval)
252 		rc = retval;
253 
254 	return rc;
255 }
256 
fd_resolve_path(int fd)257 extern char *fd_resolve_path(int fd)
258 {
259 	char *resolved = NULL;
260 	char *path = NULL;
261 
262 #if defined(__linux__)
263 	struct stat sb = {0};
264 
265 	path = xstrdup_printf("/proc/self/fd/%u", fd);
266 	if (lstat(path, &sb) == -1) {
267 		debug("%s: unable to lstat(%s): %m", __func__, path);
268 	} else {
269 		size_t name_len = sb.st_size + 1;
270 		resolved = xmalloc(name_len);
271 		if (readlink(path, resolved, name_len) <= 0) {
272 			debug("%s: unable to readlink(%s): %m",
273 			      __func__, path);
274 			xfree(resolved);
275 		}
276 	}
277 #endif
278 
279 	// TODO: use fcntl(fd, F_GETPATH, filePath) on macOS
280 
281 	xfree(path);
282 	return resolved;
283 }
284 
fd_set_oob(int fd,int value)285 extern void fd_set_oob(int fd, int value)
286 {
287 	if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &value, sizeof(value)))
288 		fatal("Unable disable inline OOB messages on socket: %m");
289 }
290 
poll_revents_to_str(const short revents)291 extern char *poll_revents_to_str(const short revents)
292 {
293 	char *txt = NULL;
294 
295 	if (revents & POLLIN)
296 		xstrfmtcat(txt, "POLLIN");
297 	if (revents & POLLPRI)
298 		xstrfmtcat(txt, "%sPOLLPRI", (txt ? "|" : ""));
299 	if (revents & POLLOUT)
300 		xstrfmtcat(txt, "%sPOLLOUT", (txt ? "|" : ""));
301 	if (revents & POLLHUP)
302 		xstrfmtcat(txt, "%sPOLLHUP", (txt ? "|" : ""));
303 	if (revents & POLLNVAL)
304 		xstrfmtcat(txt, "%sPOLLNVAL", (txt ? "|" : ""));
305 	if (revents & POLLERR)
306 		xstrfmtcat(txt, "%sPOLLERR", (txt ? "|" : ""));
307 
308 	if (!revents)
309 		xstrfmtcat(txt, "0");
310 	else
311 		xstrfmtcat(txt, "(0x%04" PRIx16 ")", revents);
312 
313 	return txt;
314 }
315 
316 /* pass an open file descriptor back to the parent process */
send_fd_over_pipe(int socket,int fd)317 extern void send_fd_over_pipe(int socket, int fd)
318 {
319 	struct msghdr msg = { 0 };
320 	struct cmsghdr *cmsg;
321 	char buf[CMSG_SPACE(sizeof(fd))];
322 	memset(buf, '\0', sizeof(buf));
323 
324 	msg.msg_iov = NULL;
325 	msg.msg_iovlen = 0;
326 	msg.msg_control = buf;
327 	msg.msg_controllen = sizeof(buf);
328 
329 	cmsg = CMSG_FIRSTHDR(&msg);
330 	cmsg->cmsg_level = SOL_SOCKET;
331 	cmsg->cmsg_type = SCM_RIGHTS;
332 	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
333 
334 	memmove(CMSG_DATA(cmsg), &fd, sizeof(fd));
335 	msg.msg_controllen = cmsg->cmsg_len;
336 
337 	if (sendmsg(socket, &msg, 0) < 0)
338 		error("%s: failed to send fd: %m", __func__);
339 }
340 
341 /* receive an open file descriptor from fork()'d child over unix socket */
receive_fd_over_pipe(int socket)342 extern int receive_fd_over_pipe(int socket)
343 {
344 	struct msghdr msg = {0};
345 	struct cmsghdr *cmsg;
346 	int fd;
347 	msg.msg_iov = NULL;
348 	msg.msg_iovlen = 0;
349 	char c_buffer[256];
350 	msg.msg_control = c_buffer;
351 	msg.msg_controllen = sizeof(c_buffer);
352 
353 	if (recvmsg(socket, &msg, 0) < 0) {
354 		error("%s: failed to receive fd: %m", __func__);
355 		return -1;
356 	}
357 
358 	cmsg = CMSG_FIRSTHDR(&msg);
359 	if (!cmsg) {
360 		error("%s: CMSG_FIRSTHDR error: %m", __func__);
361 		return -1;
362 	}
363 	memmove(&fd, CMSG_DATA(cmsg), sizeof(fd));
364 
365 	return fd;
366 }
367