@@ -880,6 +880,7 @@ version (Posix) private enum InternalError : ubyte
880
880
doubleFork,
881
881
malloc,
882
882
preExec,
883
+ closefds_dup2,
883
884
}
884
885
885
886
/*
@@ -1008,7 +1009,7 @@ private Pid spawnProcessPosix(scope const(char[])[] args,
1008
1009
if (config.flags & Config.Flags.detached)
1009
1010
close(pidPipe[0 ]);
1010
1011
close(forkPipe[0 ]);
1011
- immutable forkPipeOut = forkPipe[1 ];
1012
+ auto forkPipeOut = forkPipe[1 ];
1012
1013
immutable pidPipeOut = pidPipe[1 ];
1013
1014
1014
1015
// Set the working directory.
@@ -1042,56 +1043,106 @@ private Pid spawnProcessPosix(scope const(char[])[] args,
1042
1043
1043
1044
if (! (config.flags & Config.Flags.inheritFDs))
1044
1045
{
1045
- // NOTE: malloc() and getrlimit() are not on the POSIX async
1046
- // signal safe functions list, but practically this should
1047
- // not be a problem. Java VM and CPython also use malloc()
1048
- // in its own implementation via opendir().
1049
- import core.stdc.stdlib : malloc;
1050
- import core.sys.posix.poll : pollfd, poll, POLLNVAL ;
1051
- import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE ;
1052
-
1053
- // Get the maximum number of file descriptors that could be open.
1054
- rlimit r;
1055
- if (getrlimit(RLIMIT_NOFILE , &r) != 0 )
1056
- {
1057
- abortOnError(forkPipeOut, InternalError.getrlimit, .errno);
1058
- }
1059
- immutable maxDescriptors = cast (int ) r.rlim_cur;
1060
-
1061
- // The above, less stdin, stdout, and stderr
1062
- immutable maxToClose = maxDescriptors - 3 ;
1046
+ version (FreeBSD )
1047
+ import core.sys.freebsd.unistd : closefrom;
1048
+ else version (OpenBSD )
1049
+ import core.sys.openbsd.unistd : closefrom;
1063
1050
1064
- // Call poll() to see which ones are actually open:
1065
- auto pfds = cast (pollfd* ) malloc(pollfd.sizeof * maxToClose);
1066
- if (pfds is null )
1067
- {
1068
- abortOnError(forkPipeOut, InternalError.malloc, .errno);
1069
- }
1070
- foreach (i; 0 .. maxToClose)
1051
+ static if (! __traits(compiles, closefrom))
1071
1052
{
1072
- pfds[i].fd = i + 3 ;
1073
- pfds[i].events = 0 ;
1074
- pfds[i].revents = 0 ;
1075
- }
1076
- if (poll(pfds, maxToClose, 0 ) >= 0 )
1077
- {
1078
- foreach (i; 0 .. maxToClose)
1079
- {
1080
- // don't close pipe write end
1081
- if (pfds[i].fd == forkPipeOut) continue ;
1082
- // POLLNVAL will be set if the file descriptor is invalid.
1083
- if (! (pfds[i].revents & POLLNVAL )) close(pfds[i].fd);
1084
- }
1085
- }
1086
- else
1087
- {
1088
- // Fall back to closing everything.
1089
- foreach (i; 3 .. maxDescriptors)
1090
- {
1091
- if (i == forkPipeOut) continue ;
1092
- close(i);
1053
+ // FIXME: This implementation crashes the system when RLIMIT_NOFILE
1054
+ // has a big value. For a possible solution see:
1055
+ // https://github.com/dlang/phobos/pull/8990
1056
+ void fallback (int lowfd) {
1057
+ // NOTE: malloc() and getrlimit() are not on the POSIX async
1058
+ // signal safe functions list, but practically this should
1059
+ // not be a problem. Java VM and CPython also use malloc()
1060
+ // in its own implementation via opendir().
1061
+ import core.stdc.stdlib : malloc;
1062
+ import core.sys.posix.poll : pollfd, poll, POLLNVAL ;
1063
+ import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE ;
1064
+
1065
+ // Get the maximum number of file descriptors that could be open.
1066
+ rlimit r;
1067
+ if (getrlimit(RLIMIT_NOFILE , &r) != 0 )
1068
+ {
1069
+ abortOnError(forkPipeOut, InternalError.getrlimit, .errno);
1070
+ }
1071
+ immutable maxDescriptors = cast (int ) r.rlim_cur;
1072
+
1073
+ immutable maxToClose = maxDescriptors - lowfd;
1074
+
1075
+ // Call poll() to see which ones are actually open:
1076
+ auto pfds = cast (pollfd* ) malloc(pollfd.sizeof * maxToClose);
1077
+ if (pfds is null )
1078
+ {
1079
+ abortOnError(forkPipeOut, InternalError.malloc, .errno);
1080
+ }
1081
+ foreach (i; 0 .. maxToClose)
1082
+ {
1083
+ pfds[i].fd = i + lowfd;
1084
+ pfds[i].events = 0 ;
1085
+ pfds[i].revents = 0 ;
1086
+ }
1087
+ if (poll(pfds, maxToClose, 0 ) >= 0 )
1088
+ {
1089
+ foreach (i; 0 .. maxToClose)
1090
+ {
1091
+ // POLLNVAL will be set if the file descriptor is invalid.
1092
+ if (! (pfds[i].revents & POLLNVAL )) close(pfds[i].fd);
1093
+ }
1094
+ }
1095
+ else
1096
+ {
1097
+ // Fall back to closing everything.
1098
+ foreach (i; lowfd .. maxDescriptors)
1099
+ {
1100
+ close(i);
1101
+ }
1102
+ }
1093
1103
}
1104
+
1105
+ // closefrom may not be available on the version of glibc we build against.
1106
+ // Until we find a way to perform this check we will try to use dlsym to
1107
+ // check for the function. See: https://github.com/dlang/phobos/pull/9048
1108
+ version (CRuntime_Glibc )
1109
+ void closefrom (int lowfd) {
1110
+ static bool tryGlibcClosefrom (int lowfd) {
1111
+ import core.sys.posix.dlfcn : dlopen, dlclose, dlsym, dlerror, RTLD_LAZY ;
1112
+
1113
+ void * handle = dlopen(" libc.so.6" , RTLD_LAZY );
1114
+ if (! handle)
1115
+ return false ;
1116
+ scope (exit) dlclose (handle);
1117
+
1118
+ // Clear errors
1119
+ dlerror();
1120
+ alias closefromT = extern (C ) void function (int ) @nogc @system nothrow ;
1121
+ auto closefrom = cast (closefromT) dlsym(handle, " closefrom" );
1122
+ if (dlerror())
1123
+ return false ;
1124
+
1125
+ closefrom(lowfd);
1126
+ return true ;
1127
+ }
1128
+
1129
+ if (! tryGlibcClosefrom(lowfd))
1130
+ fallback(lowfd);
1131
+ }
1132
+ else
1133
+ alias closefrom = fallback;
1094
1134
}
1135
+
1136
+ // We need to close all open file descriptors excluding std{in,out,err}
1137
+ // and forkPipeOut because we still need it.
1138
+ // Since the various libc's provide us with `closefrom` move forkPipeOut
1139
+ // to position 3, right after STDERR_FILENO, and close all FDs following that.
1140
+ if (dup2(forkPipeOut, 3 ) == - 1 )
1141
+ abortOnError(forkPipeOut, InternalError.closefds_dup2, .errno);
1142
+ forkPipeOut = 3 ;
1143
+ // forkPipeOut needs to be closed after we call `exec`.
1144
+ setCLOEXEC(forkPipeOut, true );
1145
+ closefrom(forkPipeOut + 1 );
1095
1146
}
1096
1147
else // This is already done if we don't inherit descriptors.
1097
1148
{
@@ -1205,6 +1256,10 @@ private Pid spawnProcessPosix(scope const(char[])[] args,
1205
1256
case InternalError.preExec:
1206
1257
errorMsg = " Failed to execute preExecFunction or preExecDelegate" ;
1207
1258
break ;
1259
+ case InternalError.closefds_dup2:
1260
+ assert (! (config.flags & Config.Flags.inheritFDs));
1261
+ errorMsg = " Failed to close inherited file descriptors" ;
1262
+ break ;
1208
1263
case InternalError.noerror:
1209
1264
assert (false );
1210
1265
}
0 commit comments