nuttx-apps/system/popen/popen.c
Xiang Xiao 857158451b Unify the void cast usage
1.Remove void cast for function because many place ignore the returned value witout cast
2.Replace void cast for variable with UNUSED macro

Change-Id: Ie644129a563244a6397036789c4c3ea83c4e9b09
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2020-01-02 23:21:01 +08:00

404 lines
12 KiB
C

/****************************************************************************
* apps/popen/popen/popen.c
*
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sched.h>
#include <spawn.h>
#include <assert.h>
#include <debug.h>
#include "nshlib/nshlib.h"
/****************************************************************************
* Private Types
****************************************************************************/
/* struct popen_file_s is a cast compatible version of FILE that contains
* the additional PID of the shell processes needed by pclose().
*/
struct popen_file_s
{
FILE copy;
FILE *original;
pid_t shell;
};
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: popen
*
* Description:
* The popen() function will execute the command specified by the string
* command. It will create a pipe between the calling program and the
* executed command, and will return a pointer to a stream that can be
* used to either read from or write to the pipe.
*
* The environment of the executed command will be as if a child process
* were created within the popen() call using the fork() function, and the
* child invoked the sh utility using the call:
*
* execl(shell path, "sh", "-c", command, (char *)0);
*
* where shell path is an unspecified pathname for the sh utility.
*
* The popen() function will ensure that any streams from previous popen()
* calls that remain open in the parent process are closed in the new child
* process.
*
* The mode argument to popen() is a string that specifies I/O mode:
*
* - If mode is r, when the child process is started, its file descriptor
* STDOUT_FILENO will be the writable end of the pipe, and the file
* descriptor fileno(stream) in the calling process, where stream is
* the stream pointer returned by popen(), will be the readable end of
* the pipe.
*
* - If mode is w, when the child process is started its file descriptor
* STDIN_FILENO will be the readable end of the pipe, and the file
* descriptor fileno(stream) in the calling process, where stream is
* the stream pointer returned by popen(), will be the writable end of
* the pipe.
*
* If mode is any other value, the result is undefined.
*
* After popen(), both the parent and the child process will be capable of
* executing independently before either terminates.
*
* Pipe streams are byte-oriented.
*
* Input Parameters:
* command
*
* Returned Value:
* A non-NULLFILE stream connected to the shell instance is returned on
* success. NULL is returned on any failure with the errno variable set
* appropriately.
*
****************************************************************************/
FILE *popen(FAR const char *command, FAR const char *mode)
{
FAR struct popen_file_s *container;
struct sched_param param;
posix_spawnattr_t attr;
posix_spawn_file_actions_t file_actions;
FAR char *argv[2];
int fd[2];
int oldfd;
int newfd;
int retfd;
int errcode;
int result;
/* Allocate a container for returned FILE stream */
container = (FAR struct popen_file_s *)malloc(sizeof(struct popen_file_s));
if (container == NULL)
{
errcode = ENOMEM;
goto errout;
}
/* Create a pipe. fd[0] refers to the read end of the pipe; fd[1] refers
* to the write end of the pipe.
*/
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)
{
/* Pipe is the output from the shell */
oldfd = 1; /* Replace stdout with the write side of the pipe */
newfd = fd[1];
retfd = fd[0]; /* Use read side of the pipe to create the return stream */
}
else if (strcmp(mode, "w") == 0)
{
/* Pipe is the input to the shell */
oldfd = 0; /* Replace stdin with the read side of the pipe */
newfd = fd[0];
retfd = fd[1]; /* Use write side of the pipe to create the return stream */
}
else
{
errcode = EINVAL;
goto errout_with_pipe;
}
/* Create the FILE stream return reference */
container->original = fdopen(retfd, mode);
if (container->original == NULL)
{
errcode = errno;
goto errout_with_pipe;
}
/* Initialize attributes for task_spawn() (or posix_spawn()). */
errcode = posix_spawnattr_init(&attr);
if (errcode != 0)
{
goto errout_with_stream;
}
errcode = posix_spawn_file_actions_init(&file_actions);
if (errcode != 0)
{
goto errout_with_attrs;
}
/* Set the correct stack size and priority */
param.sched_priority = CONFIG_SYSTEM_POPEN_PRIORITY;
errcode = posix_spawnattr_setschedparam(&attr, &param);
if (errcode != 0)
{
goto errout_with_actions;
}
errcode = task_spawnattr_setstacksize(&attr, CONFIG_SYSTEM_POPEN_STACKSIZE);
if (errcode != 0)
{
goto errout_with_actions;
}
/* If robin robin scheduling is enabled, then set the scheduling policy
* of the new task to SCHED_RR before it has a chance to run.
*/
#if CONFIG_RR_INTERVAL > 0
errcode = posix_spawnattr_setschedpolicy(&attr, SCHED_RR);
if (errcode != 0)
{
goto errout_with_actions;
}
errcode = posix_spawnattr_setflags(&attr,
POSIX_SPAWN_SETSCHEDPARAM |
POSIX_SPAWN_SETSCHEDULER);
if (errcode != 0)
{
goto errout_with_actions;
}
#else
errcode = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSCHEDPARAM);
if (errcode != 0)
{
goto errout_with_actions;
}
#endif
/* Redirect input or output as determined by the mode parameter */
errcode = posix_spawn_file_actions_adddup2(&file_actions, newfd, oldfd);
if (errcode != 0)
{
goto errout_with_actions;
}
/* Call task_spawn() (or posix_spawn), re-directing stdin or stdout
* appropriately.
*/
argv[0] = (FAR char *)command;
argv[1] = NULL;
#ifdef CONFIG_SYSTEM_POPEN_SHPATH
errcode = posix_spawn(&container->shell, CONFIG_SYSTEM_POPEN_SHPATH,
&file_actions, &attr, argv,
(FAR char * const *)NULL);
#else
errcode = task_spawn(&container->shell, "popen", nsh_system, &file_actions,
&attr, argv, (FAR char * const *)NULL);
#endif
if (errcode != 0)
{
serr("ERROR: Spawn failed: %d\n", result);
goto errout_with_actions;
}
/* We can close the 'newfd' now. It is no longer useful on this side of
* the interface.
*/
close(newfd);
/* Free attributes and file actions. Ignoring return values in the case
* of an error.
*/
posix_spawn_file_actions_destroy(&file_actions);
posix_spawnattr_destroy(&attr);
/* Finale and return input input/output stream */
memcpy(&container->copy, container->original, sizeof(FILE));
return &container->copy;
errout_with_actions:
posix_spawn_file_actions_destroy(&file_actions);
errout_with_attrs:
posix_spawnattr_destroy(&attr);
errout_with_stream:
fclose(container->original);
errout_with_pipe:
close(fd[0]);
close(fd[1]);
errout_with_container:
free(container);
errout:
set_errno(errcode);
return NULL;
}
/****************************************************************************
* Name: pclose
*
* Description:
* The pclose() function will close a stream that was opened by popen(),
* wait for the command to terminate, and return the termination status of
* the process that was running the command language interpreter. However,
* if a call caused the termination status to be unavailable to pclose(),
* then pclose() will return -1 with errno set to ECHILD to report this
* situation. This can happen if the application calls one of the following
* functions:
*
* wait()
* waitpid() with a pid argument less than or equal to 0 or equal to the
* process ID of the command line interpreter
*
* Any other function not defined in this volume of IEEE Std 1003.1-2001 that
* could do one of the above
*
* In any case, pclose() will not return before the child process created by
* popen() has terminated.
*
* If the command language interpreter cannot be executed, the child termination
* status returned by pclose() will be as if the command language interpreter
* terminated using exit(127) or _exit(127).
*
* The pclose() function will not affect the termination status of any child of
* the calling process other than the one created by popen() for the associated
* stream.
*
* If the argument stream to pclose() is not a pointer to a stream created by
* popen(), the result of pclose() is undefined.
*
* Description:
* stream - The stream reference returned by a previous call to popen()
*
* Returned Value:
* Zero (OK) is returned on success; otherwise -1 (ERROR) is returned and
* the errno variable is set appropriately.
*
****************************************************************************/
int pclose(FILE *stream)
{
FAR struct popen_file_s *container = (FAR struct popen_file_s *)stream;
FILE *original;
pid_t shell;
#ifdef CONFIG_SCHED_WAITPID
int status;
int result;
#endif
DEBUGASSERT(container != NULL && container->original != NULL);
original = container->original;
/* Set the state of the original file descriptor to the state of the
* working copy
*/
memcpy(original, &container->copy, sizeof(FILE));
/* Then close the original and free the container (saving the PID of the shell
* process)
*/
fclose(original);
shell = container->shell;
free(container);
#ifdef CONFIG_SCHED_WAITPID
/* Wait for the shell to exit, retrieving the return value if available. */
result = waitpid(shell, &status, 0);
if (result < 0)
{
/* The errno has already been set */
return ERROR;
}
return status;
#else
return OK;
#endif
}