From 1ea37d48c25c10abe12ecb1e53d8820ba9d49077 Mon Sep 17 00:00:00 2001
From: Gerhard Rieger <gerhard@dest-unreach.org>
Date: Tue, 12 Nov 2024 22:28:02 +0100
Subject: [PATCH] Fixed o-creat, o-excl, and o-cloexec with POSIXMQ-*

---
 CHANGES       |  4 ++++
 doc/socat.yo  | 12 ++++------
 test.sh       | 62 ++++++++++++++++++++++++++-------------------------
 xio-fd.c      |  2 +-
 xio-file.c    |  2 +-
 xio-file.h    |  2 +-
 xio-posixmq.c | 38 ++++++++++++++++++++-----------
 xio-socket.c  |  6 ++---
 xio-udp.c     |  2 +-
 xio-unix.c    |  2 +-
 xioopts.c     | 15 +++++++------
 xioopts.h     |  4 ++--
 12 files changed, 83 insertions(+), 68 deletions(-)

diff --git a/CHANGES b/CHANGES
index ac17961..fbedf85 100644
--- a/CHANGES
+++ b/CHANGES
@@ -41,6 +41,10 @@ Corrections:
 	input lines. Changed Socat to explicitly print the newline in this
 	case.
 
+	Fixed implementation of options o-creat, o-excl, and o-cloexec with
+	POSIXMQ-* addresses.
+	POSIXMQ addresses are no longer experimental.
+
 Features:
 	POSIXMQ-RECV now takes option o-nonblock; this, in combination with -T,
 	makes it possible to terminate Socat in case the queue is empty.
diff --git a/doc/socat.yo b/doc/socat.yo
index 3786ebd..2e97524 100644
--- a/doc/socat.yo
+++ b/doc/socat.yo
@@ -779,8 +779,6 @@ label(ADDRESS_POSIXMQ_READ)dit(bf(tt(POSIXMQ-READ:/<mqueue>)))
    link(-U)(option_U) and link(dual addresses)(ADDRESS_DUAL).nl()
    Socat() provides this address type only on Linux because POSIX MQ is based
    on UNIX filedescriptors there.nl()
-   This feature is new in version 1.8.0.0 and might change in the future,
-   therefore it is link(experimental)(option_experimental).nl()
    Useful options:
    link(posixmq-priority)(OPTION_POSIXMQ_PRIORITY),
    link(unlink-early)(OPTION_UNLINK_EARLY),
@@ -793,7 +791,6 @@ dit(bf(tt(POSIXMQ-RECV:/<mqueue>)))
    This is a read-only address. See link(POSIXMQ-READ)(ADDRESS_POSIXMQ_READ)
    for more info.nl()
    Example: link(POSIX MQ recv with fork)(EXAMPLE_POSIXMQ_RECV_FORK)nl()
-   This feature is link(experimental)(option_experimental).nl()
    Useful options:
    link(posixmq-priority)(OPTION_POSIXMQ_PRIORITY),
    link(fork)(OPTION_FORK),
@@ -806,7 +803,6 @@ label(ADDRESS_POSIXMQ_SEND)dit(bf(tt(POSIXMQ-SEND:/<mqueue>)))
    This is a write-only address. See link(POSIXMQ-READ)(ADDRESS_POSIXMQ_READ)
    for more info.nl()
    (link(Example)(EXAMPLE_POSIXMQ_SEND))nl()
-   This feature is link(experimental)(option_experimental).nl()
    Useful options:
    link(posixmq-priority)(OPTION_POSIXMQ_PRIORITY),
    link(fork)(OPTION_FORK),
@@ -4458,14 +4454,14 @@ both devices. Use pppd on device tt(/var/run/ppp) then.
 label(EXAMPLE_POSIXMQ_SEND)
 mancommand(\.LP)
 mancommand(\.nf)
-mancommand(\fBsocat --experimental -u \\)
+mancommand(\fBsocat -u \\)
 mancommand(\.RS)
 mancommand(\fBSTDIO \\ 
 	POSIXMQ-SEND:/queue1,unlink-early,mq-prio=10\fP)
 mancommand(\.RE)
 mancommand(\.fi)
 
-htmlcommand(<hr><div class="shell">socat --experimental -u &#x5C;
+htmlcommand(<hr><div class="shell">socat -u &#x5C;
     STDIO &#x5C;
     POSIXMQ-SEND:/queue1,unlink-early,mq-prio=10</div>)
 
@@ -4477,7 +4473,7 @@ label(EXAMPLE_POSIXMQ_RECV_FORK)
 
 mancommand(\.LP)
 mancommand(\.nf)
-mancommand(\fBsocat --experimental -u \\)
+mancommand(\fBsocat -u \\)
 mancommand(\.RS)
 mancommand(\fBPOSIXMQ-RECV:/queue1,fork,max-children=3 \\ 
 	SYSTEM:"worker.sh"\fP)
@@ -4485,7 +4481,7 @@ mancommand(\fBPOSIXMQ-RECV:/queue1,fork,max-children=3 \\
 mancommand(\.RE)
 mancommand(\.fi)
 
-htmlcommand(<hr><div class="shell">socat --experimental -u &#x5C;
+htmlcommand(<hr><div class="shell">socat -u &#x5C;
     POSIXMQ-RECV:/queue1,fork,max-children=3 &#x5C;
     SYSTEM:"worker.sh"</div>)
 
diff --git a/test.sh b/test.sh
index 3bc44e8..f321bc0 100755
--- a/test.sh
+++ b/test.sh
@@ -1038,7 +1038,7 @@ childprocess () {
     local l
     case "$1" in
 	[1-9]*) ;;
-	*) echo "childprocess \"$1\": not a number" >&2; exit 1 ;;
+	*) echo "childprocess \"$1\": not a number" >&2; exit -1 ;;
     esac
     case "$UNAME" in
     AIX)     l="$(ps -fade |grep "^........ ...... $(printf %6u $1)")" ;;
@@ -1065,7 +1065,7 @@ childpids () {
     if [ "X$1" = "X-r" ]; then recursive=1; shift; fi
     case "$1" in
 	[1-9]*) ;;
-	*) echo "childpids \"$1\": not a number" >&2; exit 1 ;;
+	*) echo "childpids \"$1\": not a number" >&2; exit -1 ;;
     esac
     case "$UNAME" in
     AIX)     l="$(ps -fade |grep "^........ ...... $(printf %6u $1)" |awk '{print($2);}')" ;;
@@ -16702,9 +16702,9 @@ te="$td/test$N.stderr"
 tdiff="$td/test$N.diff"
 da="test$N $(date) $RANDOM"
 tq=/test$N
-CMD0a="$TRACE $SOCAT --experimental $opts -u STDIO POSIXMQ-SEND:$tq,mq-prio=0,unlink-early"
-CMD0b="$TRACE $SOCAT --experimental $opts -u STDIO POSIXMQ-SEND:$tq,mq-prio=1"
-CMD1="$TRACE $SOCAT --experimental $opts -u POSIXMQ-READ:$tq,unlink-close STDIO"
+CMD0a="$TRACE $SOCAT $opts -u STDIO POSIXMQ-SEND:$tq,mq-prio=0,unlink-early"
+CMD0b="$TRACE $SOCAT $opts -u STDIO POSIXMQ-SEND:$tq,mq-prio=1"
+CMD1="$TRACE $SOCAT $opts -u POSIXMQ-READ:$tq,unlink-close STDIO"
 printf "test $F_n $TEST... " $N
 echo "$da 0" |$CMD0a 2>"${te}0a"
 rc0a=$?
@@ -16768,9 +16768,9 @@ te="$td/test$N.stderr"
 tdiff="$td/test$N.diff"
 da="test$N $(date) $RANDOM"
 tq=/test$N
-CMD0="$TRACE $SOCAT --experimental $opts -u POSIXMQ-RECV:$tq,unlink-early,fork STDIO"
-CMD1a="$TRACE $SOCAT --experimental $opts -u STDIO POSIXMQ-SEND:$tq"
-CMD1b="$TRACE $SOCAT --experimental $opts -u STDIO POSIXMQ-SEND:$tq,unlink-close"
+CMD0="$TRACE $SOCAT $opts -u POSIXMQ-RECV:$tq,unlink-early,fork STDIO"
+CMD1a="$TRACE $SOCAT $opts -u STDIO POSIXMQ-SEND:$tq"
+CMD1b="$TRACE $SOCAT $opts -u STDIO POSIXMQ-SEND:$tq,unlink-close"
 printf "test $F_n $TEST... " $N
 $CMD0 2>"${te}0" >"${tf}0" &
 pid0=$!
@@ -16837,9 +16837,9 @@ te="$td/test$N.stderr"
 tdiff="$td/test$N.diff"
 da="test$N $(date) $RANDOM"
 tq=/test$N
-CMD0="$TRACE $SOCAT --experimental $opts -u POSIXMQ-RECV:$tq,unlink-early,fork,max-children=1 SYSTEM:\"cat\ >>${tf}0;\ sleep\ 1\""
-CMD1a="$TRACE $SOCAT --experimental $opts -u STDIO POSIXMQ-SEND:$tq"
-CMD1b="$TRACE $SOCAT --experimental $opts -u STDIO POSIXMQ-SEND:$tq,unlink-close"
+CMD0="$TRACE $SOCAT $opts -u POSIXMQ-RECV:$tq,unlink-early,fork,max-children=1 SYSTEM:\"cat\ >>${tf}0;\ sleep\ 1\""
+CMD1a="$TRACE $SOCAT $opts -u STDIO POSIXMQ-SEND:$tq"
+CMD1b="$TRACE $SOCAT $opts -u STDIO POSIXMQ-SEND:$tq,unlink-close"
 printf "test $F_n $TEST... " $N
 eval $CMD0 2>"${te}0" >"${tf}0" &
 pid0=$!
@@ -16893,8 +16893,8 @@ NAME=POSIXMQ_SEND_MAXCHILDREN
 case "$TESTS" in
 *%$N%*|*%functions%*|*%fork%*|*%maxchildren%*|*%socket%*|*%posixmq%*|*%$NAME%*)
 TEST="$NAME: POSIX-MQ SEND with fork,max-children"
-# Start a POSIX-MQ receiver that creates a POSIX-MQ and transfers data from
-# there to an output file
+# Start a POSIX-MQ receiver that creates a POSIX-MQ and transfers data from it
+# to an output file
 # Run a POSIX-MQ sender that two times forks and invokes a data generator
 # for messages 1 and 3 in a shell process with some trailing sleep.
 # Afterwards write message 2 directly into output file; message 3 should be
@@ -16911,38 +16911,40 @@ te="$td/test$N.stderr"
 tdiff="$td/test$N.diff"
 da="test$N $(date) $RANDOM"
 tq=/test$N
-CMD0="$TRACE $SOCAT --experimental $opts -u POSIXMQ-READ:$tq,unlink-early STDIO"
-CMD1="$TRACE $SOCAT --experimental $opts -U POSIXMQ-SEND:$tq,fork,max-children=1,interval=0.1 SYSTEM:\"./socat\ --experimental\ -u\ POSIXMQ-RECV\:$tq-data\ -;\ sleep\ 1\""
+CMD1="$TRACE $SOCAT $opts -u POSIXMQ-READ:$tq,unlink-early STDIO"
+CMD2="$TRACE $SOCAT $opts -U POSIXMQ-SEND:$tq,fork,max-children=1,interval=0.1 SYSTEM:\"./socat\ -u\ POSIXMQ-RECV\:$tq-data\ -;\ sleep\ 1\""
 printf "test $F_n $TEST... " $N
 # create data for the generator
-echo "$da 1" |$SOCAT -u --experimental - POSIXMQ-SEND:$tq-data,unlink-early
-echo "$da 3" |$SOCAT -u --experimental - POSIXMQ-SEND:$tq-data
-eval $CMD0 2>"${te}0" >>"${tf}0" &
-pid0=$!
-relsleep 1
-eval $CMD1 2>"${te}1" &
+echo "$da 1" |$SOCAT -u - POSIXMQ-SEND:$tq-data,unlink-early 2>"${te}0a"
+echo "$da 3" |$SOCAT -u - POSIXMQ-SEND:$tq-data 2>"${te}0b"
+eval $CMD1 2>"${te}1" >>"${tf}1" &
 pid1=$!
+relsleep 1
+eval $CMD2 2>"${te}2" &
+pid2=$!
 sleep 0.5
-echo "$da 2" >>"${tf}0"
+echo "$da 2" >>"${tf}1"
 sleep 1 	# as in SYSTEM
-kill $pid0 $(childpids $pid0) $pid1 $(childpids $pid1) 2>/dev/null
+kill $pid1 $(childpids $pid1) $pid2 $(childpids $pid2) 2>/dev/null
 wait 2>/dev/null
-$SOCAT -u --experimental /dev/null POSIXMQ-SEND:$tq-data,unlink-close
-if $ECHO "$da 1\n$da 2\n$da 3" |diff - ${tf}0 >${tdiff}0; then
+# remove the queues
+$SOCAT -u /dev/null POSIXMQ-SEND:$tq-data,unlink-close 2>"${te}3a"
+$SOCAT -u /dev/null POSIXMQ-SEND:$tq,unlink-close 2>"${te}3b"
+if $ECHO "$da 1\n$da 2\n$da 3" |diff - ${tf}1 >${tdiff}1; then
     $PRINTF "$OK\n"
-    if [ "$VERBOSE" ]; then echo "$CMD0"; fi
-    if [ "$DEBUG" ];   then cat "${te}0" >&2; fi
     if [ "$VERBOSE" ]; then echo "$CMD1"; fi
     if [ "$DEBUG" ];   then cat "${te}1" >&2; fi
+    if [ "$VERBOSE" ]; then echo "$CMD2"; fi
+    if [ "$DEBUG" ];   then cat "${te}2" >&2; fi
     ok
 else
     $PRINTF "$FAILED\n"
-    echo "$CMD0"
-    cat "${te}0" >&2
     echo "$CMD1"
     cat "${te}1" >&2
+    echo "$CMD2"
+    cat "${te}2" >&2
     echo "difference:" >&2
-    cat ${tdiff}0 >&2
+    cat ${tdiff}1 >&2
     failed
 fi
 fi # NUMCOND
diff --git a/xio-fd.c b/xio-fd.c
index 6f50629..70b8634 100644
--- a/xio-fd.c
+++ b/xio-fd.c
@@ -31,7 +31,7 @@ const struct optdesc opt_o_noinherit = { "o-noinherit", "noinherit", OPT_O_NOINH
 const struct optdesc opt_o_noatime   = { "o-noatime",   "noatime",   OPT_O_NOATIME,   GROUP_OPEN|GROUP_FD, PH_FD, TYPE_BOOL, OFUNC_FCNTL, F_SETFL, O_NOATIME };
 #endif
 /****** for ALL addresses - with fcntl(F_SETFD) ******/
-const struct optdesc opt_cloexec   = { "cloexec",   NULL, OPT_CLOEXEC,   GROUP_FD, PH_LATE, TYPE_BOOL, OFUNC_FCNTL, F_SETFD, FD_CLOEXEC };
+const struct optdesc opt_cloexec     = { "cloexec",     NULL,        OPT_O_CLOEXEC,   GROUP_FD,            PH_LATE, TYPE_BOOL, OFUNC_FCNTL, F_SETFD, FD_CLOEXEC };
 /****** ftruncate() ******/
 /* this record is good for ftruncate() or ftruncate64() if available */
 #if HAVE_FTRUNCATE64
diff --git a/xio-file.c b/xio-file.c
index c19be4b..cb61247 100644
--- a/xio-file.c
+++ b/xio-file.c
@@ -20,7 +20,7 @@ static int xioopen_open(int argc, const char *argv[], struct opt *opts, int xiof
 const struct optdesc opt_o_rdonly    = { "o-rdonly",    "rdonly", OPT_O_RDONLY,    GROUP_OPEN, PH_OPEN, TYPE_BOOL, OFUNC_FLAG_PATTERN, O_RDONLY, O_ACCMODE };
 const struct optdesc opt_o_wronly    = { "o-wronly",    "wronly", OPT_O_WRONLY,    GROUP_OPEN, PH_OPEN, TYPE_BOOL, OFUNC_FLAG_PATTERN, O_WRONLY, O_ACCMODE };
 const struct optdesc opt_o_rdwr      = { "o-rdwr",      "rdwr",   OPT_O_RDWR,      GROUP_OPEN, PH_OPEN, TYPE_BOOL, OFUNC_FLAG_PATTERN, O_RDWR,   O_ACCMODE };
-const struct optdesc opt_o_create    = { "o-create",    "creat",  OPT_O_CREATE,    GROUP_OPEN, PH_OPEN, TYPE_BOOL, OFUNC_FLAG, O_CREAT };
+const struct optdesc opt_o_creat     = { "o-creat",     "creat",  OPT_O_CREAT,     GROUP_OPEN, PH_OPEN, TYPE_BOOL, OFUNC_FLAG, O_CREAT };
 const struct optdesc opt_o_excl      = { "o-excl",      "excl",   OPT_O_EXCL,      GROUP_OPEN, PH_OPEN, TYPE_BOOL, OFUNC_FLAG, O_EXCL };
 const struct optdesc opt_o_noctty    = { "o-noctty",    "noctty", OPT_O_NOCTTY,    GROUP_OPEN, PH_OPEN, TYPE_BOOL, OFUNC_FLAG, O_NOCTTY };
 #ifdef O_SYNC
diff --git a/xio-file.h b/xio-file.h
index f592d68..be01378 100644
--- a/xio-file.h
+++ b/xio-file.h
@@ -8,7 +8,7 @@
 extern const struct optdesc opt_o_rdonly;
 extern const struct optdesc opt_o_wronly;
 extern const struct optdesc opt_o_rdwr;
-extern const struct optdesc opt_o_create;
+extern const struct optdesc opt_o_creat;
 extern const struct optdesc opt_o_excl;
 extern const struct optdesc opt_o_noctty;
 extern const struct optdesc opt_o_sync;
diff --git a/xio-posixmq.c b/xio-posixmq.c
index ff5842d..da3bdb3 100644
--- a/xio-posixmq.c
+++ b/xio-posixmq.c
@@ -20,10 +20,10 @@ static int _posixmq_unlink(const char *name, int level);
 
 static int xioopen_posixmq(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc);
 
-const struct addrdesc xioaddr_posixmq_bidir   = { "POSIXMQ-BIDIRECTIONAL", 1+XIO_RDWR,   xioopen_posixmq, GROUP_FD|GROUP_NAMED|GROUP_POSIXMQ|GROUP_RETRY,                  XIO_RDWR,   0, 0 HELP(":<mqname>") };
-const struct addrdesc xioaddr_posixmq_read    = { "POSIXMQ-READ",          1+XIO_RDONLY, xioopen_posixmq, GROUP_FD|GROUP_NAMED|GROUP_POSIXMQ|GROUP_RETRY,                  XIO_RDONLY, 0, 0 HELP(":<mqname>") };
-const struct addrdesc xioaddr_posixmq_receive = { "POSIXMQ-RECEIVE",       1+XIO_RDONLY, xioopen_posixmq, GROUP_FD|GROUP_NAMED|GROUP_POSIXMQ|GROUP_RETRY|GROUP_CHILD,      XIO_RDONLY, XIOREAD_RECV_ONESHOT, 0 HELP(":<mqname>") };
-const struct addrdesc xioaddr_posixmq_send    = { "POSIXMQ-SEND",          1+XIO_WRONLY, xioopen_posixmq, GROUP_FD|GROUP_NAMED|GROUP_POSIXMQ|GROUP_RETRY|GROUP_CHILD,      XIO_WRONLY, 0, 0 HELP(":<mqname>") };
+const struct addrdesc xioaddr_posixmq_bidir   = { "POSIXMQ-BIDIRECTIONAL", 1+XIO_RDWR,   xioopen_posixmq, GROUP_FD|GROUP_OPEN|GROUP_NAMED|GROUP_POSIXMQ|GROUP_RETRY,                  XIO_RDWR,   0, 0 HELP(":<mqname>") };
+const struct addrdesc xioaddr_posixmq_read    = { "POSIXMQ-READ",          1+XIO_RDONLY, xioopen_posixmq, GROUP_FD|GROUP_OPEN|GROUP_NAMED|GROUP_POSIXMQ|GROUP_RETRY,                  XIO_RDONLY, 0, 0 HELP(":<mqname>") };
+const struct addrdesc xioaddr_posixmq_receive = { "POSIXMQ-RECEIVE",       1+XIO_RDONLY, xioopen_posixmq, GROUP_FD|GROUP_OPEN|GROUP_NAMED|GROUP_POSIXMQ|GROUP_RETRY|GROUP_CHILD,      XIO_RDONLY, XIOREAD_RECV_ONESHOT, 0 HELP(":<mqname>") };
+const struct addrdesc xioaddr_posixmq_send    = { "POSIXMQ-SEND",          1+XIO_WRONLY, xioopen_posixmq, GROUP_FD|GROUP_OPEN|GROUP_NAMED|GROUP_POSIXMQ|GROUP_RETRY|GROUP_CHILD,      XIO_WRONLY, 0, 0 HELP(":<mqname>") };
 
 const struct optdesc opt_posixmq_priority   = { "posixmq-priority",   "mq-prio",  OPT_POSIXMQ_PRIORITY,   GROUP_POSIXMQ, PH_INIT, TYPE_BOOL, OFUNC_OFFSET, XIO_OFFSETOF(para.posixmq.prio), XIO_SIZEOF(para.posixmq.prio), 0 };
 const struct optdesc opt_posixmq_flush      = { "posixmq-flush",      "mq-flush", OPT_POSIXMQ_FLUSH,      GROUP_POSIXMQ, PH_EARLY, TYPE_BOOL, OFUNC_SPEC,   0,                               0,                             0 };
@@ -52,7 +52,11 @@ static int xioopen_posixmq(
 	struct mq_attr attr = { 0 };
 	bool setopts = false;
 	int oflag;
+	bool opt_o_creat = true;
 	bool opt_o_excl = false;
+#ifdef O_CLOEXEC
+	bool opt_o_cloexec = true;
+#endif
 	mode_t opt_mode = 0666;
 	mqd_t mqd;
 	int _errno;
@@ -61,10 +65,6 @@ static int xioopen_posixmq(
 	bool with_intv = false;
 	int result = 0;
 
-	if (!xioparms.experimental) {
-		Error1("%s: use option --experimental to acknowledge unmature state", argv[0]);
-		return STAT_NORETRY;
-	}
 	if (argc != 2) {
 		xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
 		return STAT_NORETRY;
@@ -83,7 +83,10 @@ static int xioopen_posixmq(
 			with_intv = true;
 		}
 	}
-
+	if (dirs == XIO_RDWR) {
+		/* Bidirectional ADDRESS in unidirectional mode? Adapt dirs */
+		dirs = (xioflags & XIO_ACCMODE);
+	}
 	retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);
 	if (! dofork && maxchildren) {
 		Error("option max-children not allowed without option fork");
@@ -100,8 +103,12 @@ static int xioopen_posixmq(
 		Error1("strdup(\"%s\"): out of memory", name);
 	}
 
-	retropt_bool(opts, OPT_O_EXCL, &opt_o_excl);
-	retropt_mode(opts, OPT_PERM, &opt_mode);
+	retropt_bool(opts, OPT_O_CREAT,   &opt_o_creat);
+	retropt_bool(opts, OPT_O_EXCL,    &opt_o_excl);
+#ifdef O_CLOEXEC
+	retropt_bool(opts, OPT_O_CLOEXEC, &opt_o_cloexec);
+#endif
+	retropt_mode(opts, OPT_PERM,      &opt_mode);
 	retropt_bool(opts, OPT_POSIXMQ_FLUSH, &flush);
 	retropt_long(opts, OPT_POSIXMQ_MAXMSG,  &attr.mq_maxmsg) ||
 		(setopts = true);
@@ -154,8 +161,12 @@ static int xioopen_posixmq(
 	   sfd->howtoend = END_CLOSE;
 	sfd->dtype = XIODATA_POSIXMQ | oneshot;
 
-	oflag = O_CREAT;
-	if (opt_o_excl)  oflag |= O_EXCL;
+	oflag = 0;
+	if (opt_o_creat)   oflag |= O_CREAT;
+	if (opt_o_excl)    oflag |= O_EXCL;
+#ifdef O_CLOEXEC
+	if (opt_o_cloexec) oflag |= O_CLOEXEC; 	/* does not seem to work (Ubuntu-20) */
+#endif
 	switch (dirs) {
 	case XIO_RDWR:   oflag |= O_RDWR;   break;
 	case XIO_RDONLY: oflag |= O_RDONLY; break;
@@ -185,6 +196,7 @@ static int xioopen_posixmq(
 		errno = _errno;
 		return STAT_RETRYLATER;
 	}
+	/* applyopts_cloexec(mqd, opts); */	/* does not seem to work too (Ubuntu-20) */
 	sfd->fd = mqd;
 
 	Debug1("mq_getattr(%d, ...)", mqd);
diff --git a/xio-socket.c b/xio-socket.c
index c2fb717..89045d1 100644
--- a/xio-socket.c
+++ b/xio-socket.c
@@ -799,7 +799,7 @@ int xiogetpacketinfo(struct single *sfd, int fd)
    PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECT,
    PH_CONNECTED, PH_LATE,
    OFUNC_OFFSET,
-   OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
+   OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_O_CLOEXEC
    Does not fork, does not retry.
    Alternate (alt) bind semantics are:
       with IP sockets: lowport (selects randomly a free port from 640 to 1023)
@@ -972,7 +972,7 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
    PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECT,
    PH_CONNECTED, PH_LATE,
    OFUNC_OFFSET,
-   OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
+   OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_O_CLOEXEC
    returns 0 on success.
 */
 int xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
@@ -1072,7 +1072,7 @@ int xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
    applies and consumes the following option:
    PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE
    OFUNC_OFFSET
-   OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
+   OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_O_CLOEXEC
  */
 int _xioopen_dgram_sendto(/* them is already in xfd->peersa */
 			union sockaddr_union *us, socklen_t uslen,
diff --git a/xio-udp.c b/xio-udp.c
index 98c2ced..ff5a9e1 100644
--- a/xio-udp.c
+++ b/xio-udp.c
@@ -349,7 +349,7 @@ int xioopen_udp_sendto(
    applies and consumes the following option:
    PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE
    OFUNC_OFFSET
-   OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
+   OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_O_CLOEXEC
  */
 int _xioopen_udp_sendto(const char *hostname, const char *servname,
 			struct opt *opts,
diff --git a/xio-unix.c b/xio-unix.c
index 8468fa2..0febdc6 100644
--- a/xio-unix.c
+++ b/xio-unix.c
@@ -675,7 +675,7 @@ static int xioopen_unix_client(
    PH_CONNECTED, PH_LATE, ?PH_CONNECT
    OFUNC_OFFSET,
    OPT_PROTOCOL_FAMILY, OPT_UNIX_TIGHTSOCKLEN, OPT_UNLINK_CLOSE, OPT_BIND,
-   OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_CLOEXEC, OPT_USER, OPT_GROUP, ?OPT_FORK,
+   OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_O_CLOEXEC, OPT_USER, OPT_GROUP, ?OPT_FORK,
 */
 int
 _xioopen_unix_client(
diff --git a/xioopts.c b/xioopts.c
index 0f48c40..b11cdb5 100644
--- a/xioopts.c
+++ b/xioopts.c
@@ -352,7 +352,7 @@ const struct optname optionnames[] = {
 #endif /* SO_CKSUMRECV */
 	/*IF_NAMED  ("cleanup",	&opt_cleanup)*/
 	IF_TERMIOS("clocal",	&opt_clocal)
-	IF_ANY    ("cloexec",	&opt_cloexec)
+	IF_ANY    ("cloexec",		&opt_cloexec)
 	IF_ANY    ("close",	&opt_end_close)
 	IF_OPENSSL("cn",		&opt_openssl_commonname)
 	IF_OPENSSL("commonname",	&opt_openssl_commonname)
@@ -390,8 +390,8 @@ const struct optname optionnames[] = {
 #  endif
 #endif /* defined(CRDLY) */
 	IF_TERMIOS("cread",	&opt_cread)
-	IF_OPEN   ("creat",	&opt_o_create)
-	IF_OPEN   ("create",	&opt_o_create)
+	IF_OPEN   ("creat",	&opt_o_creat)
+	IF_OPEN   ("create",	&opt_o_creat)
 	IF_ANY    ("crlf",      &opt_crnl)
 	IF_ANY    ("crnl",      &opt_crnl)
 	IF_TERMIOS("crterase",	&opt_echoe)
@@ -1135,8 +1135,8 @@ const struct optname optionnames[] = {
 #ifdef O_BINARY
 	IF_OPEN   ("o-binary",		&opt_o_binary)
 #endif
-	IF_OPEN   ("o-creat",	&opt_o_create)
-	IF_OPEN   ("o-create",	&opt_o_create)
+	IF_OPEN   ("o-creat",	&opt_o_creat)
+	IF_OPEN   ("o-create",	&opt_o_creat)
 #ifdef O_DEFER
 	IF_OPEN   ("o-defer",	&opt_o_defer)
 #endif
@@ -1194,7 +1194,8 @@ const struct optname optionnames[] = {
 #endif
 	IF_OPEN   ("o-trunc",	&opt_o_trunc)
 	IF_OPEN   ("o-wronly",	&opt_o_wronly)
-	IF_OPEN   ("o_create",	&opt_o_create)
+	IF_OPEN   ("o_creat",	&opt_o_creat)
+	IF_OPEN   ("o_create",	&opt_o_creat)
 #ifdef O_DEFER
 	IF_OPEN   ("o_defer",	&opt_o_defer)
 #endif
@@ -4230,7 +4231,7 @@ int applyopts_cloexec(int fd, struct opt *opts) {
 
    if (!opts)  return 0;
 
-   retropt_bool(opts, OPT_CLOEXEC, &docloexec);
+   retropt_bool(opts, OPT_O_CLOEXEC, &docloexec);
    if (docloexec) {
       if (Fcntl_l(fd, F_SETFD, FD_CLOEXEC) < 0) {
 	 Warn2("fcntl(%d, F_SETFD, FD_CLOEXEC): %s", fd, strerror(errno));
diff --git a/xioopts.h b/xioopts.h
index 2cdd3ac..5071cc4 100644
--- a/xioopts.h
+++ b/xioopts.h
@@ -272,7 +272,6 @@ enum e_optcode {
    OPT_CHROOT_EARLY,	/* chroot() before file system access */
    /*OPT_CIBAUD,*/		/* termios.c_cflag */
    OPT_CLOCAL,		/* termios.c_cflag */
-   OPT_CLOEXEC,
    OPT_CONNECT_TIMEOUT,	/* socket connect */
    OPT_COOL_WRITE,
    OPT_CR,		/* customized */
@@ -555,7 +554,8 @@ enum e_optcode {
    OPT_O_ASYNC,
 #endif
    OPT_O_BINARY,		/* Cygwin */
-   OPT_O_CREATE,
+   OPT_O_CLOEXEC,
+   OPT_O_CREAT,
 #ifdef O_DEFER
    OPT_O_DEFER,
 #endif