system/popen: support r+, w+ mode

The standard popen uses the pipeline internally, so the stream
returned by it can only support read-only or write-only.
Now this patch is expanded through sockpair, which can support both
read and write. Therefore, we can use the stream to read and write
access this task (posix_spawn start).

Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
This commit is contained in:
dongjiuzhu1 2023-07-29 09:51:32 +08:00 committed by Xiang Xiao
parent c10e78869b
commit e86f6c12a5

View File

@ -115,11 +115,12 @@ FILE *popen(FAR const char *command, FAR const char *mode)
posix_spawn_file_actions_t file_actions; posix_spawn_file_actions_t file_actions;
FAR char *argv[4]; FAR char *argv[4];
int fd[2]; int fd[2];
int oldfd; int oldfd[2];
int newfd; int newfd[2];
int retfd; int retfd;
int errcode; int errcode;
int result; int result = 0;
bool rw = false;
/* Allocate a container for returned FILE stream */ /* Allocate a container for returned FILE stream */
@ -130,35 +131,52 @@ FILE *popen(FAR const char *command, FAR const char *mode)
goto errout; goto errout;
} }
oldfd[1] = 0;
newfd[1] = 0;
/* Create a pipe. fd[0] refers to the read end of the pipe; fd[1] refers /* Create a pipe. fd[0] refers to the read end of the pipe; fd[1] refers
* to the write end of the pipe. * to the write end of the pipe.
* Is the pipe the input to the shell? Or the output?
*/ */
result = pipe(fd); if (strcmp(mode, "r") == 0 && (result = pipe(fd)) >= 0)
if (result < 0)
{
errcode = errno;
goto errout_with_container;
}
/* Is the pipe the input to the shell? Or the output? */
if (strcmp(mode, "r") == 0)
{ {
/* Pipe is the output from the shell */ /* Pipe is the output from the shell */
oldfd = 1; /* Replace stdout with the write side of the pipe */ oldfd[0] = 1; /* Replace stdout with the write side of the pipe */
newfd = fd[1]; newfd[0] = fd[1];
retfd = fd[0]; /* Use read side of the pipe to create the return stream */ retfd = fd[0]; /* Use read side of the pipe to create the return stream */
} }
else if (strcmp(mode, "w") == 0) else if (strcmp(mode, "w") == 0 && (result = pipe(fd)) >= 0)
{ {
/* Pipe is the input to the shell */ /* Pipe is the input to the shell */
oldfd = 0; /* Replace stdin with the read side of the pipe */ oldfd[0] = 0; /* Replace stdin with the read side of the pipe */
newfd = fd[0]; newfd[0] = fd[0];
retfd = fd[1]; /* Use write side of the pipe to create the return stream */ retfd = fd[1]; /* Use write side of the pipe to create the return stream */
} }
/* Create a socketpair. Using fd[0] as the input and output to the shell */
#if defined(CONFIG_NET_LOCAL) && defined(CONFIG_NET_LOCAL_STREAM)
else if ((strcmp(mode, "r+") == 0 || strcmp(mode, "w+") == 0) &&
(result = socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) >= 0)
{
/* Socketpair is the input/output to the shell */
rw = true;
oldfd[0] = 0; /* Replace stdin with the one side of a socket pair */
newfd[0] = fd[0];
oldfd[1] = 1; /* Replace stdout with the one side of a socket pair */
newfd[1] = fd[0];
retfd = fd[1]; /* Use other side of the socket pair to create the return stream */
}
#endif
else if (result < 0)
{
errcode = errno;
goto errout_with_container;
}
else else
{ {
errcode = EINVAL; errcode = EINVAL;
@ -236,12 +254,23 @@ FILE *popen(FAR const char *command, FAR const char *mode)
/* Redirect input or output as determined by the mode parameter */ /* Redirect input or output as determined by the mode parameter */
errcode = posix_spawn_file_actions_adddup2(&file_actions, newfd, oldfd); errcode = posix_spawn_file_actions_adddup2(&file_actions,
newfd[0], oldfd[0]);
if (errcode != 0) if (errcode != 0)
{ {
goto errout_with_actions; goto errout_with_actions;
} }
if (rw)
{
errcode = posix_spawn_file_actions_adddup2(&file_actions,
newfd[1], oldfd[1]);
if (errcode != 0)
{
goto errout_with_actions;
}
}
/* Call task_spawn() (or posix_spawn), re-directing stdin or stdout /* Call task_spawn() (or posix_spawn), re-directing stdin or stdout
* appropriately. * appropriately.
*/ */
@ -273,7 +302,12 @@ FILE *popen(FAR const char *command, FAR const char *mode)
* the interface. * the interface.
*/ */
close(newfd); close(newfd[0]);
if (rw)
{
close(newfd[1]);
}
/* Free attributes and file actions. Ignoring return values in the case /* Free attributes and file actions. Ignoring return values in the case
* of an error. * of an error.