1 /* Invoke freopen, but avoid some glitches. 2 3 Copyright (C) 2009-2013 Free Software Foundation, Inc. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18 /* Written by Eric Blake. */ 19 20 #include <config.h> 21 22 #include "stdio-safer.h" 23 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <stdbool.h> 27 #include <unistd.h> 28 29 /* Guarantee that FD is open; all smaller FDs must already be open. 30 Return true if successful. */ 31 static bool 32 protect_fd (int fd) 33 { 34 int value = open ("/dev/null", O_RDONLY); 35 if (value != fd) 36 { 37 if (0 <= value) 38 { 39 close (value); 40 errno = EBADF; /* Unexpected; this is as good as anything else. */ 41 } 42 return false; 43 } 44 return true; 45 } 46 47 /* Like freopen, but guarantee that reopening stdin, stdout, or stderr 48 preserves the invariant that STDxxx_FILENO==fileno(stdxxx), and 49 that no other stream will interfere with the standard streams. 50 This is necessary because most freopen implementations will change 51 the associated fd of a stream to the lowest available slot. */ 52 53 FILE * 54 freopen_safer (char const *name, char const *mode, FILE *f) 55 { 56 /* Unfortunately, we cannot use the fopen_safer approach of using 57 fdopen (dup_safer (fileno (freopen (cmd, mode, f)))), because we 58 need to return f itself. The implementation of freopen(NULL,m,f) 59 is system-dependent, so the best we can do is guarantee that all 60 lower-valued standard fds are open prior to the freopen call, 61 even though this puts more pressure on open fds. */ 62 bool protect_in = false; 63 bool protect_out = false; 64 bool protect_err = false; 65 int saved_errno; 66 67 switch (fileno (f)) 68 { 69 default: /* -1 or not a standard stream. */ 70 if (dup2 (STDERR_FILENO, STDERR_FILENO) != STDERR_FILENO) 71 protect_err = true; 72 /* fall through */ 73 case STDERR_FILENO: 74 if (dup2 (STDOUT_FILENO, STDOUT_FILENO) != STDOUT_FILENO) 75 protect_out = true; 76 /* fall through */ 77 case STDOUT_FILENO: 78 if (dup2 (STDIN_FILENO, STDIN_FILENO) != STDIN_FILENO) 79 protect_in = true; 80 /* fall through */ 81 case STDIN_FILENO: 82 /* Nothing left to protect. */ 83 break; 84 } 85 if (protect_in && !protect_fd (STDIN_FILENO)) 86 f = NULL; 87 else if (protect_out && !protect_fd (STDOUT_FILENO)) 88 f = NULL; 89 else if (protect_err && !protect_fd (STDERR_FILENO)) 90 f = NULL; 91 else 92 f = freopen (name, mode, f); 93 saved_errno = errno; 94 if (protect_err) 95 close (STDERR_FILENO); 96 if (protect_out) 97 close (STDOUT_FILENO); 98 if (protect_in) 99 close (STDIN_FILENO); 100 if (!f) 101 errno = saved_errno; 102 return f; 103 } 104