Background process in my own shell program to ignore stdin - shell

I'm working on my own little shell program in C. When I run the child process as a background process, I would like to ignore the input from the user coming from the parent process. I am currently trying to pipe it then close stdin for the child, but the input still goes to the child.
else // A process
{
pid_t child_pid;
char lastArgument = args[currArgsIndex-1][0];
if (lastArgument != '&'){ //Normal process
if((child_pid = fork()) == 0) {
execvp(filepath, args);
exit(0);
}
else
{
while(wait(NULL) != child_pid);
}
}
else { // Background
args[currArgsIndex-1] = NULL;
int process_pipe[2];
pipe(process_pipe); // Piping
if((child_pid = fork()) == 0) {
close(process_pipe[0]); // Ignore stdin for child
execvp(filepath, args);
exit(0);
}
}
}

You create a pipe and close the read end, but you never say that the pipe should be stdin.
It sounds like your intention was instead to 1. open the pipe only in the child, 2. close the write end so that no data can be read, 3. set the read end as stdin:
else { // Background
args[currArgsIndex-1] = NULL;
if((child_pid = fork()) == 0) {
int process_pipe[2];
pipe(process_pipe); // Piping
dup2(process_pipe[0], 0); // Copy read end as stdin
close(process_pipe[0]); // Close FD that is now unused
close(process_pipe[1]); // Close write end so no data can be read
execvp(filepath, args);
perror("execvp failed");
exit(1); // exit with error
}
}
There's no point having a pipe though. You can more easily open /dev/null for reading and setting that as stdin. Alternatively, simply close stdin entirely (some programs will complain):
else { // Background
args[currArgsIndex-1] = NULL;
if((child_pid = fork()) == 0) {
close(0); // Close stdin
execvp(filepath, args);
/* error handling */
}
Be aware that real shells allow redirecting to backgrounded processes, in which case none of the above will work:
wc -l < myfile &
Real shells will in fact not close or redirect stdin at all, but will put the command in its own process group that's not controlling the terminal. The process will then receive a SIGTSTP when it tries to read from stdin, and you can then use fg to bring it to the foreground to start typing data.

Related

Cancel IO for pipe

I 'm using the CreatePipe to redirect stdin/out from a process to my process.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365152(v=vs.85).aspx
This works ok so far. The problem is when I want to terminate the thread that waits for the client process to write something.
I can use CancelIoEx() but this only works in Vista+, and I also want an XP solution. Without CancelIoEx(), ReadFile() in the other thread never returns.
I cannot also use OVERLAPPED ReadFile, for pipes created with CreatePipe do not support it.
Any options?
Save a handle to the write end of the stdout pipe when creating the child process. You can then write a character to this to unblock the thread that has called ReadFile (that is reading from the read end of the stdout pipe). In order not to interpret this as data, create an Event (CreateEvent) that is set (SetEvent) in the thread that writes the dummy character, and is checked after ReadFile returns. A bit messy, but seems to work.
/* Init */
stdout_closed_event = CreateEvent(NULL, TRUE, FALSE, NULL);
/* Read thread */
read_result = ReadFile(stdout_read, data, buf_len, &bytes_read, NULL);
if (!read_result)
ret = -1;
else
ret = bytes_read;
if ((bytes_read > 0) && (WAIT_OBJECT_0 == WaitForSingleObject(stdout_closed_event, 0))) {
if (data[bytes_read-1] == eot) {
if (bytes_read > 1) {
/* Discard eot character, but return the rest of the read data that should be valid. */
ret--;
} else {
/* No data. */
ret = -1;
}
}
}
/* Cancel thread */
HMODULE mod = LoadLibrary (L"Kernel32.dll");
BOOL WINAPI (*cancel_io_ex) (HANDLE, LPOVERLAPPED) = NULL;
if (mod != NULL) {
cancel_io_ex = (BOOL WINAPI (*) (HANDLE, LPOVERLAPPED)) GetProcAddress (mod, "CancelIoEx");
}
if (cancel_io_ex != NULL) {
cancel_io_ex(stdout_write_pipe, NULL);
} else {
SetEvent(stdout_closed_event);
WriteFile(stdout_write_pipe, &eot, 1, &written, NULL);
}

put pipe to stdin another process

I'm using pipe to send an array of numbers to another process to sort them. So far, I'm able to get the result from another process using fdopen. However, I can't figure out how to send data from the pipe as stdin for another process.
Here is my code:
int main ()
{
int fd[2], i, val;
pid_t child;
char file[10];
FILE *f;
pipe(fd);
child = fork();
if (child == 0)
{
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execl("sort", "sort", NULL);
}
else
{
close(fd[0]);
printf ("BEFORE\n");
for (i = 100; i < 110; i++)
{
write(fd[1], &i, sizeof (int));
printf ("%d\n", i);
}
close(fd[1]);
wait(NULL);
}
}
By the way, how can the other process get input? scanf?
I think your pipe is set up correctly. The problems may start at execl(). For this call you should specify an absolute path, which is probably /bin/sort if you mean the Unix utility. There is also a version of the call execlp() which searches automatically on the PATH.
The next problem is that sort is text based, more specifically line based, and you're sending binary garbage to its STDIN.
In the parent process you should write formatted text into the pipe.
FILE *wpipe = fdopen(fd[1], "w");
for (i = 100; i < 110; i++) {
fprintf(wpipe, "%d\n", i);
...
}
fclose(wpipe);
A reverse loop from 110 down to 100 may test your sorting a bit better.

running a background process using a double fork?

pid = fork();
if(pid == 0 && background == 1){
// performs background process
pid2 = fork();
if(pid2 == 0){
// grandchild process
} else {
// child process
exit(0);
}
} else if(pid == 0){
// child process
} else {
//parent process
waitpid(pid, NULL, 0);
}
My question is I'm trying to perform a background process using double forks but I'm not 100% sure it works or not with the way I set it up, another question is after I double fork what is a good way to handle the orpaned children, can I just leave them or will that cause an issue? this is for a unix shell.

Prevent terminal prompt from printing on exec() call

SO,
There are many similar questions, however none that I have been able to use. My code snippet is as follows:
for(int j=0; j<N; j++) {
pid_t pid = fork();
if (pid == -1) {
exit(-1); //err
} else if (pid == 0) {//kid
stringstream ss;
ss<<j;
execlp("./sub","sub",ss.str().c_str(),NULL);
exit(0);
} else {
/* parent */
}
}
my executing code in sub(.cpp) is:
int main( int argc, char **argv )
{
cout<<argv[i]<<endl;
exit(0);
}
my output is as such:
[terminal prompt '$'] 4
2
3
etc.
Is there a way I could prevent the prompt from displaying on the exec call? and why is it ONLY displaying on the first exec call, and not on every one?
What you see is the normal prompt of your shell, because the parent process terminates very quickly. It is not the output of the exec call. The forked processes print their output after the parent process has terminated.
You can use waitpid() in the parent process to "wait" until all forked process have terminated.

C - passing an unknown command into execvp()

I'm writing a fake shell, where I create a child process and then call execvp(). In the normal shell, when I enter an unknown command such as 'hello' it returns 'hello: Command not found.' However, when I pass hello into execvp(), it doesn't return any error by default and just continues running the rest of my program like nothing happened. What's the easiest way to find out if nothing was actually run? here's my code:
if(fork() == 0)
{
execvp(cmd, args);
}
else
{
int status = 0;
int corpse = wait(&status);
printf(Child %d exited with a status of %d\n", corpse, status);
}
I know that if corpse < 0, then it's an unknown command, but there are other conditions in my code not listed where I don't want to wait (such as if & is entered at the end of a command). Any suggestions?
All of the exec methods can return -1 if there was an error (errno is set appropriately). You aren't checking the result of execvp so if it fails, the rest of your program will continue executing. You could have something like this to prevent the rest of your program from executing:
if (execvp(cmd, args) == -1)
exit(EXIT_FAILURE);
You also want to check the result of fork() for <0.
You have two independent concerns.
1) is the return value of execvp. It shouldn't return. If it does there is a problem. Here's what I get execvp'ing a bad command. You don't want to wait if execvp fails. Always check the return values.
int res = execvp(argv[1], argv);
printf ("res is %i %s\n", res, strerror(errno));
// => res is -1 No such file or directory
2) The other concern is background processes and such. That's the job of a shell and you're going to need to figure out when your program should wait immediately and when you want to save the pid from fork and wait on it later.

Resources