Stop some commands used in the system() function - c

I have a problem with system() function.
I need to implement a simple bash, one of the modules of the my project is to permit user types some bash command to execute it.
Above what i'm doing actually:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// similar to gets
int reads(char* str)
{
#ifndef WIN32
fflush(stdout);
strcpy(str,GetServiceLine());
#else
gets(str);
#endif
return 1;
}
int main(void) {
char str[100];
while(strcmp(str, "exit")) {
printf("\nNote: type \"exit\" to return menu\n");
printf("MyBash$ ");
reads(str);
system(str);
}
return 0;
}
My problem is with commands like ping.
When i run this code on my PC and i try execute ping command for a legal IP it works fine, i can stop the ping process using CTRL+C, but when i run it on my target on the same way i can't use CTRL+C and my process keep always at system() call.
Does somebody can help me?
Note: i read this post about how to use CTRL+C to break a system function. I tried the suggestion but didn't work.
Thanks.

Since you hadn't tried it yet I'll throw it up here as a suggestion. You can always install a signal handler to catch signals that you are interested in.
Here's a quick example using (mostly) your code which demonstrates how it's done:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void intHandler(int dummy)
{
exit(1); // Do whatever you want here to handle it...
}
int main(void)
{
char str[100];
signal(SIGINT, intHandler);
signal(SIGKILL, intHandler);
while(strcmp(str, "exit")) {
printf("\nNote: type \"exit\" to return menu\n");
printf("MyBash$ ");
gets(str);
system(str);
}
return 0;
}
I can catch a ctrl+C using this, but I'm not sure if it's what you're looking for.

After the comments above i just thought of explaining why exactly you can't control this in a graceful manner (some hacks are suggested in comments though).
system command is going to behave exactly if you forked a child process and then called exec on the child for executing the binary passed to exec as an argument.
The system() function shall ignore the SIGINT and SIGQUIT signals, and shall block the SIGCHLD signal, while waiting for the command to terminate. If this might cause the application to miss a signal that would have killed it, then the application should examine the return value from system() and take whatever action is appropriate.
Remember, this is very much OS specific behavior and there is no standard as such.
system() function call in Linux
Internally ping utility would run on icmp and waits until a response is received from the other node.
You might write a signal handler as suggested in another answer and call a killpid() but it would be blocked until the call to system() returns. This is stated in the specs of the function. So you might be able to terminate but only AFTER the call has returned. :)

Below the code used to fix my problem. I don't know if is the better solution, but solved my problem in this case.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// similar to gets
int reads(char* str)
{
#ifndef WIN32
fflush(stdout);
strcpy(str,GetServiceLine());
#else
gets(str);
#endif
return 1;
}
void getCommandName(char input[], char output[])
{
int count=0;
while (input[count] != NULL && input[count] != ' ' && input[count] != '\0') {
output[count] = input[count];
count++;
}
}
int killLastCommand(int pid)
{
char commandKill[30];
memset(commandKill, 0, 30);
sprintf(commandKill, "kill -9 %d", pid);
return(!system(commandKill));
}
int main(void) {
FILE *fp; //Will be used with popen()
char str[100];
char lastCommandName[50];
char pidofCommand[50];
char strLastPIDCommand[10];
int lastPIDCommand=0;
memset (str, 0, 100);
while(strcmp(str, "exit")) {
if (lastPIDCommand == 0) {
memset (lastCommandName, 0, 50); //Clean lastCommandName array
memset (pidofCommand, 0, 50); //Clean pidofCommand array
memset (strLastPIDCommand, 0, 10); //Clean strLastPIDCommand array
printf("\n\nNote: type \"exit\" to return menu\n");
printf("MyBash$ ");
reads(str);
if (strcmp(str, "exit")) {
sprintf(str, "%s &", str);
}
getCommandName(str, lastCommandName);
system(str);
sleep(1); //Sleep to guarantee than command will end
sprintf(pidofCommand, "pidof %s", lastCommandName);
//Saving PID
fp = popen(pidofCommand, "r");
if (fp) {
fgets(strLastPIDCommand, 10, fp);
lastPIDCommand = atoi(strLastPIDCommand);
} else {
//Handle error
}
pclose(fp);
printf("commandName = %s\r\n", lastCommandName);
printf("pid = %d\r\n", lastPIDCommand);
} else {
printf("\n\nYou have a command running, press 'kill' to stop it before to type another command\n");
printf("EITVBash$ \n\n");
reads(str);
// if (str[0] == 0x03) { //CTRL+C hexa code
if (!strcmp(str, "kill")) {
if (killLastCommand(lastPIDCommand)) {
lastPIDCommand = 0;
}
}
}
}
return 0;
}
My implementation probably isn't clean, but i don't have much experience with c.
Thanks everybody.

Related

What is the importance of adding "\n" to stdout when it's redirected to another process?

So, I'm playing with pipes in c, and I have an exercise where I call a program through command line as this: "./self 1" which then calls itself with execlp but with an argument 2: "./self 2" which further on calls itself with argument 3: "./self 3". The point of these processes is this: process1 takes a line from keyboard and puts it in pipe1, then process2 gets the line from pipe1 and puts it in pipe2, then process3 gets it from pipe2 and counts the number of space characters. This code never works if I dont print a newline character on the screen before taking inputs with fprintf(stdout,"\n"); . Why is that?
Here is the code:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
if (strcmp(argv[1], "1") == 0) {
int fdpipe1[2];
if (pipe(fdpipe1)) {
printf("Error pipe1\n");
return 0;
}
pid_t p;
p = fork();
if (p == 0) {
close(fdpipe1[1]);
dup2(fdpipe1[0], 0);
execlp("./self", "./self", "2", NULL);
} else {
close(fdpipe1[0]);
fprintf(stdout, "\n");
dup2(fdpipe1[1], 1);
char input[100];
gets(input);
puts(input);
wait(NULL);
}
}
else if (strcmp(argv[1], "2") == 0) {
int fdpipe2[2];
if (pipe(fdpipe2)) {
printf("Error pipe2\n");
return 0;
}
pid_t p;
p = fork();
if (p == 0) {
close(fdpipe2[1]);
dup2(fdpipe2[0], 0);
execlp("./self", "./self", "3", NULL);
} else {
close(fdpipe2[0]);
fprintf(stdout, "\n");
dup2(fdpipe2[1], 1);
char input[100];
gets(input);
puts(input);
wait(NULL);
}
}
else if (strcmp(argv[1], "3") == 0) {
char input[100];
gets(input);
int i = 0, counter = 0;
while (input[i] != '\0') {
if (input[i++] == ' ') counter++;
}
printf("%d\n", counter);
}
return;
}
In this kind of construct, when you connect stdout from a process to stdin of another process via unnamed pipe, a newline character is added usually to ensure the stream is sent, i.e. the stdout buffer is flushed, as a parallel example, when you use scanf, only when you hit enter (a newline is added to stdin) is the stream read, a similar principle applies here.
I would suggest you use STDIN_FILENO and STDOUT_FILENO
built in macros instead of the hard coded file descriptors, if not for anything else, it makes the code more readable for someone who is unfamiliar with the matter.
Please avoid using gets, this is a dangerous function, it does not check the bounds of the destination buffer, it can cause all kinds of trouble, so much so it was deprecated and later removed from the standard, though it still can be used with some compilers, for legacy reasons I would imagine, check this fantastic answer on a post about this topic:
Why is the gets function so dangerous that it should not be used?
The advice is to use fgets instead.

Why synchronization with pipe semaphore behaves in this strange way?

I'm trying to solve some simple example of process synchronization; in this particular example I want three processes: one prints "A", another prints "B" and the last prints "C". I want to print "A" first, then "B" or "C" and again "A", "B" or "C". To be more clear I want an output pattern like "ABACACABACABABACA...."
Below there is my symple code, termination is not managed because this is just an example to understand how things work.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void semaphore_init (int* sw){
if (pipe(sw) == -1) {
printf ("Error\n");
exit (-1);
}
}
void semaphore_wait (int* sw){
char buffer;
if (read(sw[0],&buffer,1) != 1) {
printf ("Error\n");
exit (-1);
}
}
void semaphore_signal (int* sw){
if (write(sw[1],"X",1) != 1) {
printf ("Error\n");
exit (-1);
}
}
int s1[2];
int s2[2];
void childA();
void childB();
void childC();
int main(){
pid_t pid;
semaphore_init(s1);
semaphore_init(s2);
semaphore_signal(s1);
pid=fork();
if(pid==0) childA();
else{
pid=fork();
if (pid==0) childB();
else {
pid=fork();
if(pid==0) childC();
}
}
wait(NULL);
return 0;
}
void childA(){
while(1){
semaphore_wait(s1);
printf("A-");
semaphore_signal(s2);
}
exit(0);
}
void childB(){
while(1){
semaphore_wait(s2);
printf("B-");
semaphore_signal(s1);
}
exit(0);
}
void childC(){
while(1){
semaphore_wait(s2);
printf("C-");
semaphore_signal(s1);
}
exit(0);
}
I don't understand why the output of this code is something like:
A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-C-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-A-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B
If, instead, I put the "\n" after the char I get the right output.
Could someone, kindly, explain me the reason of that?
In many implementations, including Linux, stdout is line buffered. This means any printf characters are stored to a (memory) buffer until a newline is encountered or the line buffer fills or the stream is flushed. In the example code this results in each process writing to their own local buffer every time it gains the semaphore. So even though the sequencing is correct as you intended but because there are three seperate buffers the final output result is not as you expect. Each process eventually flushes their buffer to stdout but each of those buffers only contain the same letter repeatedly.
The solution is to either add a newline to the printf (as you have found) or to do an explicit fflush(stdout). Another alternative is to use stderr which is not buffered by default: e.g. fprintf(stderr, "A-");

How to read stdout (with \r) of process opened with popen

I need to read output of a program that uses carriage returns (\r) to make its output.
I have an old utility with output like this:
#include <stdio.h>
#include <unistd.h>
int main() {
setvbuf(stdout, (char*)NULL,_IONBF, 0);
fprintf(stdout, "Start\n");
for (auto i = 0; i < 100; ++i) {
fprintf(stdout, "\r%2d", i);
usleep(100000);
}
fprintf(stdout, "\nEnd\n");
return 0;
}
So I execute it with popen from my program and I need to trace a progress:
#include <memory>
#include <stdio.h>
int main(int argc, char* argv[]) {
if (argc != 2) {
return -1;
}
std::unique_ptr<FILE, int(*)(FILE*)> filePtr( popen(argv[1], "r"), &pclose );
int c = fgetc(filePtr.get());
while (c != EOF); {
printf("%c", c);
c = fgetc(filePtr.get());
}
return 0;
}
This work fine only without \r in stdout. Does anybody know is it possible to execute a process and read all output even with carriaage return symbols?
The problem here is that the carriage-return '\r' isn't really anything special, it's how you handle it that makes it different.
A terminal program uses it to control cursor position, but that's only done inside the terminal program itself, it's not "built-in" in the '\r' character.
If you want to handle it in a special way then you need to create your program that way.

exit command does not work properly in my own shell

I wrote a shell for an assignment and it works correctly, but there is a small run time error which i can not figure out. When the user enter the command 'exit' through the shell it should come out of newly created shell. But the problem is I have to type the command 'exit' several times to quit the shell. If someone can help me it will be a great pleasure for me! Thanks all!
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
char* cmndtkn[256];
char buffer[256];
char* path=NULL;
char pwd[128];
int main(){
//setting path variable
char *env;
env=getenv("PATH");
putenv(env);
system("clear");
printf("\t MY OWN SHELL !!!!!!!!!!\n ");
printf("_______________________________________\n\n");
while(1){
fflush(stdin);
getcwd(pwd,128);
printf("[MOSH~%s]$",pwd);
fgets(buffer,sizeof(buffer),stdin);
buffer[sizeof(buffer)-1] = '\0';
//tokenize the input command line
char* tkn = strtok(buffer," \t\n");
int i=0;
int indictr=0;
// loop for every part of the command
while(tkn!=NULL)
{
if(strcoll(tkn,"exit")==0 ){
exit(0);
}
else if(strcoll(buffer,"cd")==0){
path = buffer;
chdir(path+=3);}
else if(strcoll(tkn,"|")==0){
indictr=i;}
cmndtkn[i++] = tkn;
tkn = strtok(NULL," \t\n");
}cmndtkn[i]='\0';
// execute when command has pipe. when | command is found indictr is greater than 0.
if(indictr>0){
char* leftcmnd[indictr+1];
char* rightcmnd[i-indictr];
int a,b;
for(b=0;b<indictr;b++)
leftcmnd[b]=cmndtkn[b];
leftcmnd[indictr]=NULL;
for(a=0;a<i-indictr-1;a++)
rightcmnd[a]=cmndtkn[a+indictr+1];
rightcmnd[i-indictr]=NULL;
if(!fork())
{
fflush(stdout);
int pfds[2];
pipe(pfds);
if(!fork()){
close(1);
dup(pfds[1]);
close(pfds[0]);
execvp(leftcmnd[0],leftcmnd);
}
else{
close(0);
dup(pfds[0]);
close(pfds[1]);
execvp(rightcmnd[0],rightcmnd);
}
}else wait(NULL);
//command not include pipe
}else{
if(!fork()){
fflush(stdout);
execvp(cmndtkn[0],cmndtkn);
}else wait(NULL);
}
}
}
Like the cd command, the exit command has to be interpreted by the shell as a built-in; it must exit the loop or call the exit() function directly. However, it also appears that should be happening. Note that using strcoll() is a little unusual; normally, strcmp() is sufficient.
You should report problems if execvp() returns — and you must make sure the sub-shell exits so that you don't have multiple shell processes reading the input simultaneously. I'm left wondering if this problem is occurring, and that's why you have to type exit multiple times.
You also need to check that fgets() did not report an error. It always null terminates its input; your code does not zap the newline (you'd need strlen(buffer)-1 instead of sizeof(buffer)-1).
The code that reads and sets PATH is wrong. getenv("PATH") returns a pointer to the first character after the PATH= part; you then use that to 'set' the environment. Fortunately for you, the average value for PATH does not contain anything that looks like VAR=value, so it is functionally a no-op (though the information is probably copied into the environment, where it makes a mess without causing any major harm).
Your code indentation scheme is rococo at best — mostly, it is just woefully inconsistent. Please be consistent! The spacing of the lines in the code was also extremely erratic. When you're adding code in SO, do not use tabs, do use 4 spaces per indent level, do highlight a block of code that is left justified and use the {} button above the edit box to indent it as code. This also means you don't need to add blank lines to the code.
You aren't closing enough file descriptors. When you use dup() (or dup2()) to duplicate a pipe to standard input or standard output, you have to close both of the file descriptors returned by pipe().
On Linux, using fflush(stdin) is undefined behaviour, AFAIK. It is defined on Windows, but not on POSIX systems.
You don't check that your chdir() system call works.
Trying your code, I did get one runaway prompt. Unfortunately, I couldn't remember or see what triggered the runaway. The code below is mostly sanitized and seems to behave. I've annotated some critical changes — and not others. One of the things you should be doing for your own benefit is including trace like the dump_cmd() function so you can see what your program is doing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
char *cmndtkn[256];
char buffer[256];
char *path = NULL;
char pwd[128];
static void dump_cmd(char **argv);
int main(void)
{
/*
//setting path variable
char *env;
env=getenv("PATH");
putenv(env);
system("clear");
*/
printf("\t MY OWN SHELL !!!!!!!!!!\n ");
printf("_______________________________________\n\n");
while (1)
{
//fflush(stdin);
getcwd(pwd, 128);
printf("[MOSH~%s]$", pwd);
if (fgets(buffer, sizeof(buffer), stdin) == 0)
{
putchar('\n');
break;
}
//buffer[sizeof(buffer)-1] = '\0';
buffer[strlen(buffer)-1] = '\0';
//tokenize the input command line
char *tkn = strtok(buffer, " \t\n");
int i = 0;
int indictr = 0;
// loop for every part of the command
while (tkn != NULL)
{
if (strcoll(tkn, "exit") == 0)
{
printf("Got: exit\n");
fflush(stdout);
exit(0);
}
else if (strcoll(tkn, "cd") == 0) // Was buffer, not tkn
{
printf("Got: cd (%s)\n", buffer + 3);
fflush(stdout);
path = buffer;
chdir(path += 3);
}
else if (strcoll(tkn, "|") == 0)
{
indictr = i;
}
cmndtkn[i++] = tkn;
tkn = strtok(NULL, " \t\n");
}
cmndtkn[i] = 0;
// execute when command has pipe. when | command is found indictr is greater than 0.
if (indictr > 0)
{
char *leftcmnd[indictr+1];
char *rightcmnd[i-indictr];
int a, b;
for (b = 0; b < indictr; b++)
leftcmnd[b] = cmndtkn[b];
leftcmnd[indictr] = NULL;
for (a = 0; a < i-indictr-1; a++)
rightcmnd[a] = cmndtkn[a+indictr+1];
rightcmnd[i-indictr-1] = NULL; // Did not include -1
if (!fork())
{
fflush(stdout);
int pfds[2];
pipe(pfds);
if (!fork())
{
dump_cmd(leftcmnd);
close(1);
dup(pfds[1]);
close(pfds[0]);
close(pfds[1]);
execvp(leftcmnd[0], leftcmnd);
fprintf(stderr, "failed to execvp() %s\n", leftcmnd[0]);
exit(1);
}
else
{
dump_cmd(rightcmnd);
close(0);
dup(pfds[0]);
close(pfds[0]);
close(pfds[1]);
execvp(rightcmnd[0], rightcmnd);
fprintf(stderr, "failed to execvp() %s\n", rightcmnd[0]);
exit(1);
}
}
else
wait(NULL);
}
else
{
//command does not include pipe
if (!fork())
{
dump_cmd(cmndtkn);
fflush(stdout);
execvp(cmndtkn[0], cmndtkn);
fprintf(stderr, "failed to execvp() %s\n", cmndtkn[0]);
exit(1);
}
else
wait(NULL);
}
}
return 0;
}
static void dump_cmd(char **argv)
{
int i = 0;
fprintf(stderr, "%d: Command:\n", (int)getpid());
while (*argv != 0)
fprintf(stderr, "%d: %d: [[%s]]\n", (int)getpid(), i++, *argv++);
}
I'm not keen on the code, but it does seem mostly sane.

I have a problem with the WIFSIGNALED()/WTERMSIG() macros, after using waitpid()

In this code C i launch a program from the command line and when it is closed from a signal different from SIGTERM (signal for normal end) my code should relaunch the initial program passed from the command line. But it is not so, in fact my code never relaunchs program saying that it is properly terminated.In practice my condition"if(WTERMSIG(status)!=SIGTERM)" works bad, WHY????? :'(
This is my code:
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char*argv[])
{
pid_t pid;
int* status=(int*)malloc(sizeof(int));
int term;
if(argc<2)
{
printf("Error: Too few arguments\n");
exit(EXIT_FAILURE);
}
while(1)
{
pid=fork();
if(pid!=0) /*father*/
{
waitpid(pid,status,0);
//term=WIFSIGNALED(status);
if(WIFSIGNALED(status))
{
if(WTERMSIG(status)!=SIGTERM)
{
printf("The program %d ended abnormally:\nRelaunching...\n",pid);
sleep(1);
}
else
printf("The program %d is properly terminated...\n",pid);
break;
}
else
{
printf("Can not read the reason for termination\n");
}
}
else /*child*/
{
execvp(argv[1],argv+1);
exit(EXIT_SUCCESS);
}
}
return 1;
}
The WIFSIGNALED() and WTERMSIG() macros both expect plain ints, not pointers to ints. This means that in your code, where status is a pointer to an int, you need to use *status when calling the macros, to pass them the value of the integer.
That said: why are you calling malloc() to allocate room for a single int, anyway? Just use a normal variable, and &status if you need a pointer to it.
Also, you should return EXIT_SUCCESS from main() on successful completion of your program, not 1.

Resources