How to make system() function unblocking? - linux

I am calling an executable from another executable in android Linux. Following is the relevant code:
...
int status = system("/system/bin/executable");
...
My requirement is not to wait for the executable to complete its execution. Means I want to run executable independent of the executable that calls it.
I have searched over the internet and didn't find how to make this system call non-blocking. Please help me to resolve it.

The system() function, without error handling, looks like this:
int system(char const *cmdline)
{
pid_t pid = fork();
if(pid == 0)
{
char const *argv[] = { "sh", "-c", cmdline, NULL };
execve("/bin/sh", argv, NULL);
_exit(1);
}
else
{
int status;
waitpid(pid, &status, 0);
return status;
}
}
The command itself is parsed by the shell, so you can use the normal & suffix to send the command into the background. The shell then terminates immediately, the background program is reparented to PID 1 (so your program isn't responsible for collecting the zombie), and system() returns.

I am able to achieve non-blocking with following code:
if (fork() == 0)
{
char *args[] = {..., NULL};
char *env[] = {..., NULL};
if (execve("/system/bin/executable", args, env) == -1)
print("Error: [%d]", errno);
}
There are few importants thing here:
fork() will create a new process. So from line if(fork() == 0), there will be 2 process running in the same space of main program.
Both processes continue executing from the point where the fork( ) calls returns execution to the main program..
fork() == 0 will let only child process in the if condition.
execve(..) will replace child process program(which is its parent program from which it copied by fork command) with /system/bin/executable.
execve(..) will not return if it get success in runing the executable else return -1.
In case of execve(..) failure the errno will be filled with the actual error.
Please correct me if I am wrong. I hope it will help someone.

Related

How to invoke an executable in the path /usr/bin using a C++ program?

I have an GUI based executable in the path /usr/bin in the linux machine
This executable takes three arguments - two integer values and one char
Can you let me know how to invoke and run this executable from a user space C++ program
Not leaving this unanswered for no reason
pid_t runprocess(int arg1, int arg2, char arg3)
{
static const char program[] = "/usr/bin/...";
char arg1c[12];
char arg2c[12];
char arg3c[2];
sprintf(arg1c, "%d", arg1);
sprintf(arg2c, "%d", arg2);
arg3c[0] = arg3;
arg3c[1] = 0;
pid_t pid = vfork();
if (pid == 0) {
signal(SIGHUP, SIG_IGN); /* since it's a GUI program, detach from console HUP */
close(0); /* and detach from stdin */
if (open("/dev/null", O_RDWR)) _exit(137); /* assertion failure */
execl(program, program, arg1c, arg2c, arg3c, NULL);
_exit(errno);
}
return pid;
}
Build arguments as strings, fork and exec it. Trivial really. Don't forget to wait().
Since the child process is a GUI process, we detach HUP from the terminal we may or may not be running on and replace stdin with /dev/null.

How does execl deal with "/bin/sh" in Linux?

I read about APUE 3rd, 8.13, system Function, and I saw a version of system function implementation without signal handling.Code is like below:
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
int system(const char *cmdstring) /* version without signal handling */
{
pid_t pid;
int status;
if (cmdstring == NULL)
return(1); /* always a command processor with UNIX */
if ((pid = fork()) < 0) {
status = -1; /* probably out of processes */
} else if (pid == 0) { /* child */
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127); /* execl error */
} else { /* parent */
while (waitpid(pid, &status, 0) < 0) {
if (errno != EINTR) {
status = -1; /* error other than EINTR from waitpid() */
break;
}
}
}
return(status);
}
And the code used to test system function of this version is like below:
int main(void)
{
int status;
if ((status = system("date")) < 0)
err_sys("system() error");
pr_exit(status);
if ((status = system("nosuchcommand")) < 0)
err_sys("system() error");
pr_exit(status);
if ((status = system("who; exit 44")) < 0)
err_sys("system() error");
pr_exit(status);
exit(0);
}
And the result of the test code is shown by the picture(just ignore the Chinese in the result if you can't understand):
I wonder why will execl return if "nosuchcommand", which is not valid for /bin/sh, is given to /bin/sh. In my point of view, execl just replace the code of current process and then run from entry point, even though "nosuchcommand" is not valid for /bin/sh, it has nothing to do with execl but /bin/sh. So, how execl know "nosuchcommand" is not valid for /bin/sh to execute and return? Does execl treat /bin/sh differently by checking the command given to /bin/sh before executing /bin/sh so that it will know the invalid argument given to /bin/sh in advance? I know execl won't treat /bin/sh differently, so, how does execl know "nosuchcommand" is not valid for /bin/sh to execute and return?
sh -c nosuchcommand itself returns 127. It's one of those return codes with a special meaning.
So I don't think you're seeing execl actually returning in this case.
It doesn't "know". It simply executes what you tell it to. /bin/sh then reports that it cannot find it, after which /bin/sh exits with non-zero exit code, in this case 127.
Also note that you cannot depend on it returning exactly 127 as that is shell-specific. Some shells (including /bin/sh on some OSes) will return 1 instead.

How to synchronize input and output in pipes linux?

I am creating a shell command from the custom shell to do the ssh from one terminal to another terminal.
In order to do the ssh, I am using the inbuilt ssh command of the linux. Here is my code that does the ssh login.
However, I am seeing that the I/O buffers are not in sync.
This is what I am seeing on the terminal. After SSH to the other terminal. I did the following in the terminal.
PRT# ssh 192.168.10.42
PRT# Could not create directory '/root/.ssh'.
root#192.168.10.42's password:
# screen -r
-sh: cen-: not found
# hello
-sh: el: not found
#
I don't what's the reason here. Here is the code.
int sshLogin(chr *destIp)
{
char cmd[CMD_LEN];
char readbuff[CMD_LEN];
pid_t pid;
int ret = 0;
int fd[2];
int result;
memset(cmd,'\0',sizeof(cmd));
int status = 0;
/** --tt required to force pseudowire allocation because we are behind screen app **/
sprintf(cmd,"/usr/bin/ssh -tt %s",destIp);
/** create a pipe this will be shared on fork() **/
pipe(fd);
if((pid = fork()) == -1)
{
perror("fork:");
return -1;
}
if( pid == 0 )
{
/** Child Process of Main APP --Make this parent process for the command**/
if((pid = fork()) == -1)
{
perror("fork:");
return -1;
}
if( pid == 0)
{
/** basically Main APP grand child - this is where we running the command **/
ret = execlp("ssh", "ssh", "-tt", destIp, NULL);
printf("done execlp\r\n");
}
else
{
/** child of Main APP -- keep this blocked until the Main APP grand child is done with the job **/
while( (read(fd[0], readbuff, sizeof(readbuff))))
{
printf("%s",readbuff);
}
waitpid(0,&status,0);
LOG_STRING("SSH CONNC CLOSED");
exit(0);
}
}
else
{
/** Parent process APP MAIN-- **/
/** no need to wait let APP MAIN run -- **/
}
return 0;
}
Based on Patrick Ideas.
POST 2# - It seems that it works when we close the stdin in the parent process. However, it becomes very slugguish, I feel like I am typing the keyboard too slow. The system becomes too sluggish. Also, I have a web-server from this terminal. I see that I can no longer access the web.
So, the solution is somewhere around stdin but I am not sure.
int sshLogin(chr *destIp)
{
char cmd[CMD_LEN];
char readbuff[CMD_LEN];
pid_t pid;
int ret = 0;
int fd[2];
int result;
memset(cmd,'\0',sizeof(cmd));
int status = 0;
/** --tt required to force pseudowire allocation because we are behind screen app **/
sprintf(cmd,"/usr/bin/ssh -tt %s",destIp);
/** create a pipe this will be shared on fork() **/
pipe(fd);
if((pid = fork()) == -1)
{
perror("fork:");
return -1;
}
if( pid == 0 )
{
/** Child Process of Main APP --Make this parent process for the command**/
if((pid = fork()) == -1)
{
perror("fork:");
return -1;
}
if( pid == 0)
{
/** basically Main APP grand child - this is where we running the command **/
ret = execlp("ssh", "ssh", "-tt", destIp, NULL);
printf("done execlp\r\n");
}
else
{
/** child of Main APP -- keep this blocked until the Main APP grand child is done with the job **/
while( (read(fd[0], readbuff, sizeof(readbuff))))
{
printf("%s",readbuff);
}
waitpid(0,&status,0);
LOG_STRING("SSH CONNC CLOSED");
exit(0);
}
}
else
{
/** Parent process APP MAIN-- **/
/** no need to wait let APP MAIN run -- **/
close(stdin);
}
return 0;
}
Basically, I have added - close(stdin);
You have 2 different processes trying to read from STDIN. This causes process 1 to get char 1, process 2 to get char 2, process 1 to get char 3, process 2 to get char 4, etc, alternating back and forth.
Your 2 processes are:
execlp("ssh", "ssh", "-tt", destIp, NULL);.
while( (read(fd[0], readbuff, sizeof(readbuff))))
Basically you need to ditch the read(fd[0],...).
My initial thought is that perhaps it is buffering the output: stdout is buffered, so unless you print a newline, nothing will be printed until a certain number of characters build up. This is because I/O operations are expensive. You can find more detail on this here. The result is that there is a delay because your program is waiting to print.
My suggestion: in your main function, before calling your sshLogin function, try disabling buffering with this line of code:
setbuf(stdout, NULL);
You can also call fflush(stdout); periodically to do the same thing, but the above method is more efficient. Try it and see if that solves your problem.

Linux/OSX/Windows - Waiting end of process

Under OSX, there is "open -W" which allows to wait the end of an executable.
What is the equivalent instruction for windows ?
Linux does have this non-blocking behaviour with programs like sublime-text (subl). How does it do that (execv ?)
Basically, I'm trying ,within a C program, to launch an executable and wait until it ends up.
With the edit, under linux, you're looking at:
childpid = fork();
if (childpid) {
execve("program", argvp, envp);
} else {
int status;
pid_t pid = wait(&status);
}
under windows, you need to use CreateProcess to create the process, and then use WaitForSingleObject to wait for the process to terminate; e.g.
PROCESS_INFORMATION processInformation = {0};
STARTUPINFO startupInfo = {0};
startupInfo.cb = sizeof(startupInfo);
bool status = CreateProcess(L"Program", L"args", 0, 0, 0,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
0, 0, &startupInfo, &processInformation);
if (status) {
WaitForSingleObject(processInformation.hProcess, INFINITE);
}
Under Linux, a parent process can wait for a child process to terminate by using the wait or waitpid system call. For more finer process synchronization, use semaphores.

Unexplainable behaviour with replecating manual piping using dup2

I have two sets of code both trying to execute something like ls|grep pip
One that works and one that does not.
The working code creates 2 child process and uses one child each to execlp the one command and the other simply tries to do this by creating one child. I.e executing ls in say the child and the grep in the parent. This does not seem to work. And I can't seem to get any error either.
Can someone tell me what the problem is? And why it exists?
Not Working:
void runpipe()
{
pid_t childpid;
int fd[2];
pipe(fd);
int saved_stdout;
int saved_stdin;
saved_stdout=dup(STDOUT_FILENO);
saved_stdin=dup(STDIN_FILENO);
if((childpid=fork())==0)
{
dup2(fd[WRITE_END],STDOUT_FILENO);
close(fd[WRITE_END]);
execlp("/bin/ls","ls command","-l",NULL);
dup2(STDOUT_FILENO,fd[1]);
_exit(0);
}
else if(childpid>0)
{
dup2(saved_stdout,STDOUT_FILENO);
dup2(fd[READ_END],STDIN_FILENO);
close(fd[READ_END]);
execlp("/bin/grep","grep","pip",NULL);
wait();
_exit(0);
}
else
{
printf("ERROR!\n");
}
}
Here are the codes:
Working:
int runpipe(){
pid_t pid;
int fd[2];
pipe(fd);
int i;
pid=fork();
if (pid==0) {
printf("i'm the child used for ls \n");
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[READ_END]);
execlp("ls", "ls", "-al", NULL);
_exit(0);
} else {
pid=fork();
if (pid==0) {
printf("i'm in the second child, which will be used to grep\n");
dup2(fd[READ_END], STDIN_FILENO);
close(fd[WRITE_END]);
execlp("grep", "grep","pip",NULL);
}
else wait();
}
return 0;
}
The parent needs to close the write side of the pipe before exec'ing grep. For some reason, your code with the two children closes that file descriptor, but does not in the code with only one child. You are leaving several descriptors open, but the write side on the pipe is the important one. The reader (the exec'd grep) will not finish until all copies of the write side of the pipe are closed. By failing to close it, the grep is the one holding it open so grep will never terminate, but just wait for more data.

Resources