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