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;
FAR char *argv[4];
int fd[2];
int oldfd;
int newfd;
int oldfd[2];
int newfd[2];
int retfd;
int errcode;
int result;
int result = 0;
bool rw = false;
/* Allocate a container for returned FILE stream */
@ -130,35 +131,52 @@ FILE *popen(FAR const char *command, FAR const char *mode)
goto errout;
}
oldfd[1] = 0;
newfd[1] = 0;
/* Create a pipe. fd[0] refers to the read end of the pipe; fd[1] refers
* to the write end of the pipe.
* Is the pipe the input to the shell? Or the output?
*/
result = pipe(fd);
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)
if (strcmp(mode, "r") == 0 && (result = pipe(fd)) >= 0)
{
/* Pipe is the output from the shell */
oldfd = 1; /* Replace stdout with the write side of the pipe */
newfd = fd[1];
oldfd[0] = 1; /* Replace stdout with the write side of the pipe */
newfd[0] = fd[1];
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 */
oldfd = 0; /* Replace stdin with the read side of the pipe */
newfd = fd[0];
oldfd[0] = 0; /* Replace stdin with the read side of the pipe */
newfd[0] = fd[0];
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
{
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 */
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)
{
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
* appropriately.
*/
@ -273,7 +302,12 @@ FILE *popen(FAR const char *command, FAR const char *mode)
* the interface.
*/
close(newfd);
close(newfd[0]);
if (rw)
{
close(newfd[1]);
}
/* Free attributes and file actions. Ignoring return values in the case
* of an error.