summaryrefslogtreecommitdiffstats
path: root/src/master/dup2-array.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/master/dup2-array.c')
-rw-r--r--src/master/dup2-array.c78
1 files changed, 78 insertions, 0 deletions
diff --git a/src/master/dup2-array.c b/src/master/dup2-array.c
new file mode 100644
index 0000000..062590a
--- /dev/null
+++ b/src/master/dup2-array.c
@@ -0,0 +1,78 @@
+/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "dup2-array.h"
+
+#include <unistd.h>
+
+void dup2_append(ARRAY_TYPE(dup2) *dups, int fd_src, int fd_dest)
+{
+ struct dup2 d;
+
+ i_assert(fd_src >= 0);
+ i_assert(fd_dest >= 0);
+
+ d.fd_src = fd_src;
+ d.fd_dest = fd_dest;
+ array_push_back(dups, &d);
+}
+
+int dup2_array(ARRAY_TYPE(dup2) *dups_arr)
+{
+ struct dup2 *dups;
+ bool *moved, moves;
+ unsigned int i, j, count, conflict;
+ int fd;
+
+ dups = array_get_modifiable(dups_arr, &count);
+
+ moved = t_new(bool, count);
+ for (;;) {
+ conflict = count;
+ moves = FALSE;
+ for (i = 0; i < count; i++) {
+ if (moved[i])
+ continue;
+
+ for (j = 0; j < count; j++) {
+ if (dups[j].fd_src == dups[i].fd_dest &&
+ !moved[j]) {
+ conflict = j;
+ break;
+ }
+ }
+
+ if (j == count) {
+ /* no conflicts, move it */
+ moved[i] = TRUE;
+ moves = TRUE;
+ if (dup2(dups[i].fd_src, dups[i].fd_dest) < 0) {
+ i_error("dup2(%d, %d) failed: %m",
+ dups[i].fd_src,
+ dups[i].fd_dest);
+ return -1;
+ }
+ }
+ }
+ if (conflict == count)
+ break;
+
+ if (moves) {
+ /* it's possible that the conflicting fd was
+ moved already. try again. */
+ continue;
+ }
+
+ /* ok, we have to dup() */
+ fd = dup(dups[conflict].fd_src);
+ if (fd == -1) {
+ i_error("dup(%d) failed: %m", dups[conflict].fd_src);
+ return -1;
+ }
+ fd_close_on_exec(fd, TRUE);
+ dups[conflict].fd_src = fd;
+ }
+ return 0;
+}
+