A regular C program, an implementation of some shell program, which uses standard input to get commands and executes them, in which main() is declared as: int main(int argc, char *argv[]), works normally.
It's a small shell program that prints a prompt and waits for user input in a loop, and executes the command entered. However I want to make the commandline parameters invisible when invoked, as follows:
rename the main() function to old_main().
create a new main() function that:
Copies the argv[] string array to a new array, and then
Erases the original argv[] structure.
Forks itself and in the child calls old_main with the saved commandline parameters, and exits the parent process.
This works as 'ps -ef' now shows the command run without the commandline arguments displayed.
However, the program now behaves different, and does not wait for user-input, but keeps printing the prompt. Even Ctrl-C does not work anymore.
If, however, I call the program as:
$ echo quit | ./newprog
The command 'quit' is executed (causing the program to exit), so the program still works when the stdin is redirected, but does not work in interactive mode.
Below the new program that forks itself and in the child calls the old main with the saved commandline parameters.
/* newprog.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_NARGS 40
extern int old_main(int nargc, char *nargv[]);
int main(int argc, char *argv[])
{
int i; pid_t pid;
int nargc=argc;
char *nargv[];
nargv[0]=argv[0];
for (i = 1; i < argc; i++)
{
nargv[i]=strdup(argv[i]);
memset(argv[i],0,strlen(argv[i]));
}
nargv[i]=NULL;
pid=fork();
if (pid == 0) return old_main(nargc, nargv); /* child calls old_main() */
else if (pid == -1) printf("error: fork()\n");
exit(0); /* exit parent */
}
Related
I am writing my own simple shell. One thing which I need to do is to control the SIGINT signal by remaining in the shell and just printing the prompt on a fresh line when ctrl+c is pressed. Currently, I have been able to handle the signal and the shell simply prints ^C after the prompt. However, the cursor remains on the same line. What I would like to do instead is have the shell print ^C after the prompt, move onto the next line and then print a fresh prompt.
I have found this question, which addresses the exact same issue. The problem with mine is that my main calls another function where the prompt loop is run. I have tried many different ways to try to implement the solution given on the link above, in both the main and the prompt loop function, but all with no luck. Here is my code so far:
Main.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "MyShell.h"
void ctrl_C_handler();
int main(int argc, char **argv) {
signal(SIGINT, ctrl_C_handler);
my_shell_loop();
return EXIT_SUCCESS;
}
void ctrl_C_handler() {
//Catches the SIGINT signal fine without anything happening in this function
//I cannot figure out how to have MyShell print a fresh prompt on a new line
//after ctrl+C is pressed
}
MyShellLoop.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "MyShell.h"
char *get_time();
void my_shell_loop() {
char *line;
char **args;
int status;
char *prompt = (char *) malloc(17);
do {
strcpy(prompt, get_time());
strcat(prompt, " # ");
printf("%s", prompt);
line = read_command();
args = split_command(line);
status = execute_command(args);
free(line);
free(args);
} while (status);
free(prompt);
}
EDIT
Using:
void ctrl_C_handler() {
signal(SIGINT, ctrl_C_handler);
printf("\n");
my_shell_loop();
}
acts as desired the first time ctrl+c is pressed, but then acts as it did before for any further times it is pressed.
signal attaches a handler only for the first appropriate signal that is received. After that invocation, the handler is detached. A common method is to have the handler reattach itself as in int foo() { signal(SIGINT, &foo); do_the_stuff(); }.
However, signal is non-portable. POSIX recommends using sigaction instead.
I need to create a program that takes input from the user and executes it just like it does in the terminal. I am using the execvp() function for this purpose. The requirement of the program is to keep taking input from the user unless the quit call is encountered. The problem here is that the current program is replaced after the execvp() call. So, using a goto is not an option either. I found this Fork–exec article but it doesn't tell how to create an indefinite number of processes. Here is my code:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void main() {
char *args[4];
char inputCommand[100];
fgets (inputCommand, 100, stdin);
printf ("Splitting string \"%s\" into tokens:\n",inputCommand);
/* Perfrom string tokenization here */
execvp(args[0], args);
}
fork() can be called an indefinite number of times; as long as the return value indicates that you're the parent process, you can continue to operate as usual and call it again.
Thus, you can have a loop within which you fork, call an execvp() if you're the child process, and continue to the next iteration if you're the parent.
Execvp replaces the current process image with the command you run. So it cancels your C program. To produce the desired effect, you should fork before execvp. It would look something like this:
int status = 0;
pid_t pid = fork();
if(pid > 0) {
waitpid(pid, &status, 0);
// is parent
} else if(pid == 0) {
execvp(*args, args);
// is child
} else {
// fork didn't work
}
I'm having problems understanding the right use of the pipe in UNIX Systems.
I have a main process which create a child process. The child process must run a different program from the father, he has to make some operation and then the child must communicate to the father the results.
However in the child process I have to print on the terminal the partial results of these operations.
I'm trying with a test program to do so, but I'm a bit stuck right now. This is the main test program
TEST.C
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
int mypipe[2];
pid_t pid;
char readbuffer[6];
pipe(mypipe);
if((pid = fork()) == 0){
close(mypipe[0]);
dup2(mypipe[1], STDOUT_FILENO);
execlp("./proc", "./proc", NULL);
} else {
wait(-1);
close(mypipe[1]);
read(mypipe[0], readbuffer, sizeof(readbuffer));
printf("%s", readbuffer);
}
}
And the c file of the ./proc program is this:
PROC.C
#include <stdio.h>
int main(int argc, char* argv[]){
printf("check\n");
return 0;
}
With this solution, the proc program can't print anything on the terminal. How do I make the proc program to print on the terminal AND on the pipe so the main program can read from there???
Thank you!
Even when you redirect stdout to the parent program, you can still use stderr. Just call fprintf(stderr, ...) instead of printf(...) when you want to print to the console.
If you want your "proc" program to print in terminal for log/debug infos, u can use fprintf and stderr:
fprintf(stderr, "What you need to print\n");
You can also see where your program is writing, use strace
I need some help emulating the "|" command in unix. I need to be able to use the output from the first argument as the input of the second, something as simple as ls and more. I got this code so far but I'm just stuck at this point. Any and all help would be helpful.-Thanks.
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char ** words)
{
char** args;
char *cmd1[2] = { words[1], 0 };
char *cmd2[2] = { words[2], 0 };
int colon, arg1 ,i, pid, status;
int thepipe[2];
char ch;
args = (char **) malloc(argc*(sizeof(char*)));
colon = -1;
for (i=0;(i<argc); i=i+1){
if (strcmp(words[i],":") == 0) {
colon = i;
}
else {}
}
pipe(thepipe);
arg1 = colon;
arg1 = arg1 - 1;
for (i=0;(i<arg1); i=i+1){
args[i] = (char*) (malloc(strlen(words[i+1])+1));
strcpy(args[i], words[i+1]);
}
args[argc] = NULL;
pid = fork();
if (pid == 0) {
wait(&pid);
dup2(thepipe[1], 1);
close(thepipe[0]);
printf("in new\n");
execvp(*args, cmd1);
}
else {
close(thepipe[1]);
printf("in old\n");
while ((status=read(thepipe[0],&ch,1)) > 0){
execvp(*args, cmd2);
}
}
}
Assuming that argv[1] is a single word command (like ls) and argv[2] is a second single word command (like more), then:
Parent
Create a pipe.
Fork first child.
Fork second child.
Close both ends of the pipe.
Parent waits for both children to die, reports their exit status, and exits itself.
Child 1
Duplicates write end of pipe to standard output.
Close both ends of the pipe.
Uses execvp() to run the command in argv[1].
Exits, probably with an error message written to standard error (if the execvp() returns).
Child 2
Duplicates read end of pipe to standard input.
Close both ends of the pipe.
Uses execvp() to run the command in argv[2].
Exits, probably with an error message written to standard error (if the execvp() returns).
The only remaining trick is that you need to create a vector such as:
char cmd1[2] = { argv[1], 0 };
char cmd2[2] = { argv[2], 0 };
to pass as the second argument to execvp().
Note that this outline does not break the strings up. If you want to handle an invocation such as:
./execute "ls -la" "wc -wl"
then you will need to split each argument into separate words and create bigger arrays for cmd1 and cmd2. If you want to handle more than two commands, you need to think quite carefully about how you're going to manage the extra stages in the pipeline. The first and last commands are different from those in the middle (so 3 processes has three different mechanisms, but 4 or more substantially uses the same mechanism over and over for all except the first and last commands).
I am trying to learn how to use the pipe() command in C, and trying to create a test program to duplicate the functionality of ls | grep ".c", if I were to enter this into a linux terminal. If I enter this into the terminal, I only get test.c as a result.
My code is as follows:
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "fcntl.h"
int main(int argc, char** argv)
{
int pipefd[2];
int childpid,childpid2;
char* cmd[3]={"ls",NULL,NULL};
char* cmd2[3]={"grep",".c",NULL};
pipe(pipefd);
if(childpid=fork()){
//parent
}else{
//child
//write
close(pipefd[0]);
dup2(pipefd[1],STDOUT_FILENO);
execvp("ls", cmd);
}
if(childpid2=fork()){
}
else{
close(pipefd[1]);
dup2(pipefd[0],STDIN_FILENO);
execvp("grep",cmd2);
}
close(pipefd[0]);
close(pipefd[1]);
return 0;
}
This code returns me the following results ($ is the terminal prompt):
$a.out
$test.c
(blank line)
The program doesn't complete, but hangs until I quit it. What problems do I have? How can I mimic the terminal? I'm new to C, and using a premade template of a program, so forgive me if there are glaring mistakes.
Try this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char** argv)
{
int pipefd[2];
int childpid,childpid2;
char* cmd[3]={"ls",NULL,NULL};
char* cmd2[3]={"grep",".c",NULL};
pipe(pipefd);
if(childpid=fork()){
//parent
close(pipefd[1]);
dup2(pipefd[0],STDIN_FILENO);
execvp("grep",cmd2);
}else{
//child
//write
close(pipefd[0]);
dup2(pipefd[1],STDOUT_FILENO);
execvp("ls", cmd);
}
return 0;
}
Actually the program exits right away — in fact, the parent process exits before the children run, which is why there's a shell prompt before "test.c".
You can improve things a bit by adding this in your parent:
wait(childpid);
wait(childpid2);
which will make the parent exit after both children.
Your program quits immediately, while your processes run in the background. They overwrite the prompt, and makes you think the program is still running even though the shell is waiting for your input (press enter or type a command blindly and see.
You only see test.c because that's the only matching file in your directory (also note that you're checking for "filenames containing c anywhere except the first character", not "ending in .c" which would be grep '\.c$').
The simple fix is to add:
wait(NULL); wait(NULL);
right before your return 0.
This question is a bit old, but here's an older answer that was never provided. Use libpipeline. libpipeline is a pipeline manipulation library. The use case is one of the man page maintainers who had to frequently use a command like the following (and work around associated OS bugs):
zsoelim < input-file | tbl | nroff -mandoc -Tutf8
Here's the libpipeline way:
pipeline *p;
int status;
p = pipeline_new ();
pipeline_want_infile (p, "input-file");
pipeline_command_args (p, "zsoelim", NULL);
pipeline_command_args (p, "tbl", NULL);
pipeline_command_args (p, "nroff", "-mandoc", "-Tutf8", NULL);
status = pipeline_run (p);
The libpipeline has more examples on its homepage. The library is also included in many distros, including Arch, Debian, Fedora, Linux from Scratch and Ubuntu.