@@ -153,6 +153,21 @@ main (int argc, char **argv)
153
153
sigaddset (& block_cld , SIGCHLD );
154
154
sigprocmask (SIG_BLOCK , & block_cld , NULL );
155
155
156
+ /* In some cases rlimit might be launched with stdin and/or stdout being
157
+ * a terminal. In that case at process startup, the rlimit process is
158
+ * the foreground process group. This means that the following actions
159
+ * will cause the child process to block:
160
+ * - read input from the terminal (SIGTTIN signal)
161
+ * - write output to the terminal
162
+ * (SIGTTOU signal if terminal mode TOSTOP enabled)
163
+ * - change terminal settings (SIGTTOU signal)
164
+ *
165
+ * As rlimit is not supposed to read input the following is done:
166
+ *
167
+ * 1- make the child process the foreground process group
168
+ * 2- allow the parent process to write in parallel to the terminal
169
+ */
170
+
156
171
pid = fork ();
157
172
switch (pid ) {
158
173
case -1 :
@@ -184,6 +199,23 @@ main (int argc, char **argv)
184
199
}
185
200
#endif /* __APPLE__ */
186
201
202
+ /* ignore SIGTTOU so that tcsetpgrp call does not block. */
203
+ signal (SIGTTOU , SIG_IGN );
204
+
205
+ /* Set child process as foreground process group so that
206
+ * children can read from the terminal if needed. Note that
207
+ * we ignore any error here because stdin might not be a terminal.
208
+ * If only stdout is a terminal there are no issues with not
209
+ * being the foreground process as most terminals are configured
210
+ * with TOSTOP off (so SIGTTOU is only emited in case of terminal
211
+ * settings change.
212
+ */
213
+ tcsetpgrp (0 , getpgid (getpid ()));
214
+
215
+ /* Restore SIGTTIN and SIGTTOU signal to their original value
216
+ * in order not to impact children processes behaviour. */
217
+ signal (SIGTTIN , SIG_DFL );
218
+ signal (SIGTTOU , SIG_DFL );
187
219
execvp ((const char * ) argv [2 ], (char * const * ) & argv [2 ]);
188
220
fprintf (stderr , "rlimit: could not run \"%s\": " , argv [2 ]);
189
221
perror ("execvp" );
@@ -196,6 +228,13 @@ main (int argc, char **argv)
196
228
int timeout = atoi (argv [1 ]);
197
229
int seconds = timeout ;
198
230
231
+ /* At this stage rlimit might not be anymore the foreground process.
232
+ * Ignore SIGTTOU in order to able to write and SIGTTIN for safety
233
+ * (though no input is attempted from the rlimit itself).
234
+ */
235
+ signal (SIGTTOU , SIG_IGN );
236
+ signal (SIGTTIN , SIG_IGN );
237
+
199
238
/* pid variable is now set correctly so unblock SIGCHLD */
200
239
sigprocmask (SIG_UNBLOCK , & block_cld , NULL );
201
240
0 commit comments