clearing an array in preparation for new array values - arrays

The function gets an input from the user using read() and breaks it down using strtok and places it into an array. The program loops until it reaches an error (which i won't get into because that isn't the problem here) or if the program is terminated by the user. However, when it loops back around and reads the input from the user, it seems to be hanging onto the previous input from the user.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#define BUFFERSIZE 1024
int main(int argc, char* argv[])
{
argc++;
char buf[BUFFERSIZE];
int n;
printf("Please enter commands: \n");
while ((n = read(STDIN_FILENO, buf, BUFFERSIZE)) > 0)
{
printf("original string: %s:\n", buf);
buf[strlen(buf)-1] = '\0';
printf("after change: %s:\n", buf);
int i = 0;
char* array[100];
char* token1 = strtok(buf, " ");
while ((token1 != NULL))
{
array[i++] = token1;
token1 = strtok(NULL, " ");
}//while
for (int j = 0; j < i; j++)
{
printf("Array value %d: %s:\n", j, array[j]);
}//for
if (buf == "exit")
{
printf("found it\n");
}//if
for (int i = 1; i < argc; i++)
{
pid_t pid;
if (argc >= 1)
{
if ((pid = fork()) < 0)
{
perror("fork");
}//if
else if (pid == 0)
{ // child process
if (execvp(array[0], array) == -1)
{
perror("execvp");
return EXIT_FAILURE;
} // if
}//else if
else
{ // parent process
int status;
wait(&status);
printf("Please enter commands again: \n");
}//else
}//if
else
{
fprintf(stderr, "Please specify the name of the program to exec as a command line argument\n");
return EXIT_FAILURE;
}//if
}//for
}//while
if (n == -1) perror("read");
}//main
I've tried to clear the array and clear "buf" but no luck. i have a feeling it has to do with the read() and the fact that "buf" is hanging onto its old value.

Prefaced by my top comments ...
read does not add 0x00 the way fgets does, so we have to do it manually.
execvp needs the array to be terminated with a NULL entry.
child should use exit instead of return.
Here's the refactored code. It is annotated. Note that this code does not do the split/join of the buffer to guarantee the buffer ending in newline as suggested by my top comments:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#define BUFFERSIZE 1024
int
main(int argc, char *argv[])
{
argc++;
char buf[BUFFERSIZE];
int n;
printf("Please enter commands: \n");
// NOTE/BUG: read does _not_ add 0x00 the way fgets does
#if 0
while ((n = read(STDIN_FILENO, buf, BUFFERSIZE)) > 0) {
#else
while ((n = read(STDIN_FILENO, buf, BUFFERSIZE - 1)) > 0) {
buf[n] = 0;
#endif
printf("original string: %s:\n", buf);
buf[strlen(buf) - 1] = '\0';
printf("after change: %s:\n", buf);
int i = 0;
char *array[100];
char *token1 = strtok(buf, " ");
while ((token1 != NULL)) {
array[i++] = token1;
token1 = strtok(NULL, " ");
}
// NOTE/BUG: execvp needs a NULL terminator
#if 1
array[i] = NULL;
#endif
for (int j = 0; j < i; j++) {
printf("Array value %d: %s:\n", j, array[j]);
}
// NOTE/BUG: wrong way to compare strings
#if 0
if (buf == "exit")
#else
if (strcmp(buf, "exit") == 0) {
printf("found it\n");
break;
}
#endif
for (int i = 1; i < argc; i++) {
pid_t pid;
if (argc >= 1) {
if ((pid = fork()) < 0) {
perror("fork");
}
// child process
else if (pid == 0) {
if (execvp(array[0], array) == -1) {
perror("execvp");
#if 0
return EXIT_FAILURE;
#else
exit(EXIT_FAILURE);
#endif
}
}
// parent process
else {
int status;
wait(&status);
printf("Please enter commands again: \n");
}
}
else {
fprintf(stderr, "Please specify the name of the program to exec as a command line argument\n");
return EXIT_FAILURE;
}
}
}
if (n == -1)
perror("read");
}
In the above code, I've used cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k

Related

Formating execvp output

I am working on a shell command program in C, I have it working but the output is not formatted correctly. I am unable to see where the problem lies. I have read through the code several times and I am not seeing the issue. I have tried placing \n in various places but that typicality results in worse formatting.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <wait.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#define MAX_LINE 80 /*Maximum length of a command*/
#define MAX_HST 10
int main(void){
char getInput[MAX_LINE];
char *args[MAX_LINE/2+1]; /*command line arguments*/
int should_run = 1; /*Flag to determine when to exit the program*/
int numCommand = 0;
int cmdHst = 0;
char *cmdHistory[MAX_HST];
char *myCmd;
while (should_run = 1){
printf("osh> ");
fflush(stdout);
fgets(getInput, MAX_LINE, stdin);//read input command
if(strcmp(getInput, "!!\n") == 0){//Command history
if(cmdHst == 0){
printf("No previous commands.\n");
}
for (int i= 0; i<cmdHst; i++){//display all commands in history
strncpy(getInput, cmdHistory[i], MAX_LINE);
printf("%s\n", getInput);
}
}
if (cmdHst < MAX_HST){
cmdHistory[cmdHst] = strdup(getInput);
cmdHst++;
}
else{//shift commands in history to fill gaps
for(int i = 1; i < cmdHst; i++){
free(cmdHistory[i-1]);
cmdHistory[i-1]= strdup(cmdHistory[i]);
}
free(cmdHistory[cmdHst-1]);
cmdHistory[MAX_HST-1] = strdup(getInput);
}
//parsing commands into tokens
numCommand = 0;
args[numCommand] = strtok(getInput, " \n");
while(args[numCommand] != NULL){
numCommand++;
args[numCommand] = strtok(NULL, " \n");
}
args[numCommand]=NULL;
if(strcmp(args[0], "exit")==0){//Check for exit command
should_run =0;
break;
}
pid_t pid = fork();//Create child process
if(pid < 0){
fprintf(stderr, "Fork Failed\n");
return 1;
}
else if(pid == 0){
if(execvp(args[0], args)==-1){
fprintf(stderr, "Command Not Found.\n");
exit(1);
}
else{
if(args[numCommand-1][0] != '&'){
wait(NULL);
}
}
}
}
for(int i = 0; i < cmdHst; i++){//free histrry array
free(cmdHistory[i]);
}
return 0;
}
I have read over the code a few times and tried in putting fprintf("\n");s in various places.
I forgot the parent wait(NULL) command. That fixed it.

Scanf through pipe lock

I have an exercise where I need to interact with a C program through pipe.
I have the following source, which I can't modify.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int number;
int answer;
number = rand() % 100;
printf("Print the double of the number %d\n", number);
scanf("%d", &answer);
if(number * 2 == answer)
printf("Success\n");
else
printf("Error\n");
}
I tried to interact with this program with this code
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv, char **env)
{
int STDIN_PIPE[2];
int STDOUT_PIPE[2];
pipe(STDIN_PIPE);
pipe(STDOUT_PIPE);
pid_t pid = fork();
if(pid == 0)
{
char *path = "/path/to/binary";
char *args[2];
args[0] = path;
args[1] = NULL;
close(STDIN_PIPE[1]);
close(STDOUT_PIPE[0]);
dup2(STDIN_PIPE[0], STDIN_FILENO);
dup2(STDOUT_PIPE[1], STDOUT_FILENO);
execve(path, args, env);
}
else
{
char buf[128];
close(STDIN_PIPE[0]);
close(STDOUT_PIPE[1]);
while(read(STDOUT_PIPE[0], buf, 1))
write(1, buf, 1);
}
}
But when I run it, it falls in an infinite loop without printing nothing.
I have fixed a number of issues in your code, added a lot of error checks and completed it so that the end goal is reached.
In the child process, srand() must be called to initialize the random number generator or you always get the same value.
The in the child process, you must flush(stdout) after printing the question so that it is really written to the pipe.
And finally, scanf() return value must be checked.
In the main process, I added a lot of error checks. And I write a readLine function to - guess what - read a line from the pipe. A line is terminated by the end-of-line character \n.
There is still room for some enhancements...
I tested my code using Visual Studio Code configured for gcc and running under Ubuntu 20.04.
Here is the child process source:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int number;
int answer;
time_t t;
srand((unsigned)time(&t));
number = rand() % 100;
printf("Print the double of the number %d\n", number);
fflush(stdout);
int n = scanf("%d", &answer);
if (n != 1) {
printf("Invalid input\n");
return 1;
}
if ((number * 2) == answer) {
printf("Success\n");
return 0;
}
printf("Error %d is not 2 * %d\n", answer, number);
return 1;
}
And here is the main process source:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int readLine(int fd, char *buf, int bufSize);
int main(int argc, char **argv, char **env)
{
int STDIN_PIPE[2];
int STDOUT_PIPE[2];
if (pipe(STDIN_PIPE))
{
perror("pipe(STDIN_PIPE)");
return 1;
}
if (pipe(STDOUT_PIPE)) {
perror("pipe(STDOUT_PIPE)");
return 1;
}
pid_t pid = fork();
if (pid == 0)
{
char *path = "../Child/Child"; // Path to child process, adapt to your environment
char *args[2];
args[0] = path;
args[1] = NULL;
if (dup2(STDIN_PIPE[0], STDIN_FILENO) == -1) {
perror("dup2(STDIN) failed");
return 1;
}
if (dup2(STDOUT_PIPE[1], STDOUT_FILENO) == -1) {
perror("dup2(STDIN) failed");
return 1;
}
// Close all pipe ends
close(STDIN_PIPE[0]); // Close read end of STDIN_PIPE
close(STDIN_PIPE[1]); // Write end of STDIN_PIPE
close(STDOUT_PIPE[0]); // Read end of STDOUT_PIPE
close(STDOUT_PIPE[1]); // Close write end of STDOUT_PIPE
if (execve(path, args, env) == -1) {
perror("execve failed");
return 1;
}
}
else
{
char buf[128];
int bufSize = sizeof(buf) / sizeof(buf[0]);
int i;
// Read the question asked by child process
if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
printf("readLine failed.\n");
return 1;
}
// We receive something like "Print the double of the number 83"
printf("Child process question is \"%s\".\n", buf);
// Extract the number at end of string
i = strlen(buf) - 1;
while ((i >= 0) && isdigit(buf[i]))
i--;
int value = atoi(buf + i + 1);
// Write our answer to write end of STDIN_PIPE
char answer[128];
int answerSize = sizeof(answer) / sizeof(answer[0]);
int answerLen = snprintf(answer, answerSize, "%d\n", value * 2);
printf("Our answer is \"%d\".\n", value * 2);
if (write(STDIN_PIPE[1], answer, answerLen) != answerLen) {
printf("write failed.\n");
return 1;
}
// Read the response (success or failure) sent by child process
if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
printf("readLine failed.\n");
return 1;
}
if (strcasecmp(buf, "Success") == 0)
printf("Child process returned success.\n");
else
printf("Child process returned failure.\n");
// Close all pipe ends
close(STDIN_PIPE[0]); // Close read end of STDIN_PIPE
close(STDIN_PIPE[1]); // Write end of STDIN_PIPE
close(STDOUT_PIPE[0]); // Read end of STDOUT_PIPE
close(STDOUT_PIPE[1]); // Close write end of STDOUT_PIPE
}
return 0;
}
// Read a line from file descriptor
// A line is any characters until \n is received or EOF
// \n is not kept
// Return the number of characters read or <0 if error:
// -1 => Input buffer overflow
// -2 => read() failed and errno has the error
int readLine(int fd, char *buf, int bufSize)
{
int i = 0;
while (1)
{
// Check if enough room in the buffer
if (i >= bufSize) {
printf("Input buffer overflow\n");
return -1;
}
// Read one character from the pipe
ssize_t n = read(fd, buf + i, 1);
if (n == -1)
{
perror("read() failed");
return -2;
}
if (n == 0)
{
// EOF received, that's OK
return i;
}
// NUL terminate the buffer
buf[i + 1] = 0;
// Check for end of line character
if (buf[i] == '\n') {
buf[i] = 0; // Remove ending \n
return i;
}
i++;
}
}

execvp() not working in my shell

I am trying to make a tiny shell. My problem is that when I call execvp() - I get errors.
For example, when I type in ls -l it returns ls: invalid option -- '
Can someone, please, help me understand why I am getting this error? For my code, the function command split gets the user input, and splits them up into separate commands. Separate commands are seperated by ; character.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAX_CHARACTERS 512
#define HISTORY_SIZE 10
int commandSplit(char *c, char *a[], int t[]) {
int count = 0;
int total = 0;
char *temp[MAX_CHARACTERS];
char *readCommands = strtok(c, ";");
while(readCommands != NULL) {
printf("Reading full command: %s\n", readCommands);
temp[count] = readCommands;
count++;
readCommands = strtok(NULL, ";");
}
printf("Done reading full commands\n");
for(int i = 0; i < count; i++) {
char *read = strtok(temp[i], " ");
int track = 0;
while(read != NULL) {
printf("Reading individual command: %s\n", read);
a[total] = read;
track++;
total++;
read = strtok(NULL, " ");
}
t[i] = track;
}
return count;
}
int main() {
int exitProgram = 0;
char *args[MAX_CHARACTERS];
while(!exitProgram) {
char *commands = (char *)(malloc(MAX_CHARACTERS*sizeof(char)));
int tracker[MAX_CHARACTERS];
int numOfCommands = 0;
printf("tinyshell> ");
fgets(commands, MAX_CHARACTERS, stdin);
if(strlen(commands) == 0) continue;
numOfCommands = commandSplit(commands, args, tracker);
printf("There are %i commands!\n", numOfCommands);
if(strcmp(args[0], "exit") == 0) {
printf("Exiting\n");
exitProgram = 1;
continue;
}
int l = 0;
for(int i = 0; i < numOfCommands; i++) {
int status;
char *holder[tracker[i]+1];
for(int j = 0; j < tracker[i]; j++) {
holder[j] = args[l];
printf("Assiging holder:%s\n", holder[j]);
l++;
}
holder[tracker[i]] = NULL;
printf("What is holder? \n");
for(int o = 0; o < tracker[i]; o++) printf("%s", holder[o]);
pid_t p = fork();
pid_t waiting;
if(p == 0) {
printf("I am in child process\n");
execvp(holder[0], holder);
fprintf(stderr, "Child process could not execvp!\n");
exit(1);
}
else {
if(p < 0) {
fprintf(stderr, "Fork FAILED!\n");
}
else {
waiting = wait(&status);
printf("Child %d, status %d\n", waiting, status);
}
}
for(int i = 0; i < numOfCommands; i++) {
args[i] = NULL;
}
}
}
return 0;
}
Your problem is that fgets() also reads the newline character. As a result, the last argument of execvp() arguments array contains a newline, causing ls complain about an unrecognized argument: what you acctually pass to ls is -l\n; what you need to pass is just -l without the newline.
Try adding this code after the fgets call to trim the input buffer:
int len;
len = strlen(commands);
if (len > 0 && commands[len-1] == '\n') {
commands[len-1] = '\0';
}

Implementing unlimited piping in shell using C

I'm trying to implement a C shell that allows for unlimited unidirectional pipes using the character '>'
So it can handle ls -A > tail > grep '.zip'
I understand that pipes are supposed to talk between processes, but I thought I came up with an idea that could use one pipe and multiple children.
This is what I have so far
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
/*#include <wait.h>*/
char *args[1000][1000];//array of arguments
int args_count = 0;//count of the arguments in the array
int runCommand(char **arguments, int *fd, int pipeHasSomeData, int baseCase) {
pid_t pid;
int x = 0;
int status;
pid = fork();
if(pid != 0) {
waitpid(pid, &status, 0);
if(baseCase) {
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
/*it worked*/
} else if(WEXITSTATUS(status) == 255) {
printf("The program %s does not exist \n", arguments[0]);
} else {
printf("ERROR: Error code: %d", WEXITSTATUS(status));
}
}
else
{
printf("There was a problem that is not normal");
}
printf("\n \n");
}
return 1;
} else {
if(pipeHasSomeData == 1) {// read from the pipe
dup2(fd[0], 0);//read from pipe
}
if(baseCase == 0) {// not the base case
dup2(fd[1], 1);//write to pipe
} else {
close(fd[1]);//close write
}
exit(execvp(arguments[0], arguments));
return 0;
}
}
int execute_commands(char *arguments[1000][1000], int pd[2] = NULL) {
int current_count = args_count;
int iterator = 0;
int fd[2];
int useAPipeInCommand = 0;
pipe(fd);
while(iterator <= args_count) {//go through and execute all the commands
if(current_count == 0) {//base case
return runCommand(arguments[iterator], fd, useAPipeInCommand, 1);
} else {
runCommand(arguments[iterator], fd, useAPipeInCommand, 0);
useAPipeInCommand = 1;
}
iterator++;
current_count--;
}//end while
return 1;
}
int main () {
int i = 0;
char text[1024]; /* the input line */
char *tok2;
while (1) { /* repeat until done .... */
fflush(stdin);
fflush(stdout);
printf("Shell -> "); /* display a prompt */
*text = 0;
fgets(text, sizeof text, stdin); /* read in the command line */
fflush(stdout);
printf("\n");
char * tok = strtok(text, " \n\t");
if (strcmp(tok, "exit") == 0) { /* is it an "exit"? */
return 0; /* exit if it is */
}
if (strcmp(tok, " ") == 0) { /* is it an "exit"? */
continue; /* exit if it is */
}
tok2 = tok;
memset(args, 0, sizeof(args[0][0]) * 1000 * 1000);//clear the arguments array
args_count = 0;
int count = 0;
while(tok2 != NULL) {
if(strcmp(tok2, ">") != 0) {
args[args_count][count] = tok2;
count++;
tok2 = strtok(NULL, " \n\t");
} else {//pipe was found, up the argument counter and set count to 0
args[args_count][count] = NULL;
args_count++;
count = 0;
tok2 = strtok(NULL, " \n\t");
}
}
args[args_count][count] = NULL;
execute_commands(args);
}//end while
return 0;
}
It is running the single base case no problem but the shell freezes when I do a pipe. Any ideas on the issue?
Correct answer from Comments by #beau-bouchard and #rici:
Pipes have a (small) finite buffer; you cannot write more than a little bit to the pipe without blocking unless the other end of the pipe is being read.
For a correct implementation, check out "multiple pipes in C" Coding multiple pipe in C
--UPDATE:
Here is my final working code for anyone that is having a similar issue:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <wait.h>
int READ = 0;
int WRITE = 1;
char *args[1000][1000];//array of arguments
int args_count = 0;//count of the arguments in the array
int execute_commands(char *arguments[1000][1000]) {
int pd[2];
int iterator = 0;
int fd[2];
int f_in = 0;
while(iterator <= args_count) {//go through and execute all the commands
pid_t pid;
int status;
pipe(fd);
pid = fork();
if(pid != 0) {
waitpid(pid, &status, 0);//wait for child to exit
close(fd[WRITE]);//close the writing end
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
/*it worked*/
} else if(WEXITSTATUS(status) == 255) {
printf("The program %s does not exist \n", arguments[iterator][0]);
} else {
printf("ERROR: Error code: %d", WEXITSTATUS(status));
}
}
else
{
printf("There was a problem that is not normal %d", status);
}
f_in = fd[READ];//set the pipe to the in
if(iterator == args_count) {
printf("\n \n");
}
//return 1;
} else {
dup2(f_in, 0);
if(iterator != args_count) {//its not the main value
dup2(fd[WRITE], 1);//write to pipe
}
close(fd[READ]);
exit(execvp(arguments[iterator][0], arguments[iterator]));
return 0;
}
iterator++;
}//end while
return 1;
}
int main () {
int i = 0;
char text[1024]; /* the input line */
char *tok2;
while (1) { /* repeat until done .... */
fflush(stdin);
fflush(stdout);
printf("Shell -> "); /* display a prompt */
*text = 0;
fgets(text, sizeof text, stdin); /* read in the command line */
fflush(stdout);
printf("\n");
char * tok = strtok(text, " \n\t");
if (strcmp(tok, "exit") == 0) { /* is it an "exit"? */
return 0; /* exit if it is */
}
if (strcmp(tok, " ") == 0) { /* is it an "exit"? */
continue; /* exit if it is */
}
tok2 = tok;
memset(args, 0, sizeof(args[0][0]) * 1000 * 1000);//clear the arguments array
args_count = 0;
int count = 0;
while(tok2 != NULL) {
if(strcmp(tok2, ">") != 0) {
args[args_count][count] = tok2;
count++;
tok2 = strtok(NULL, " \n\t");
} else {//pipe was found, up the argument counter and set count to 0
args[args_count][count] = NULL;
args_count++;
count = 0;
tok2 = strtok(NULL, " \n\t");
}
}
args[args_count][count] = NULL;
execute_commands(args);
}//end while
return 0;
}

I am making linux command Program by C-language. and I want to know what is wrong the code?

I wanna ask about how to make exec process programing by C.
Now, I typed like these code, and I use strtok and strdup.
my code wrong assign value from input, so could you see my code and could you teach what is wrong in the code.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
int main(){
int pid;
int status, i = 0;
char input[256], path[30];
const char* a[10];
char* token;
char* split = " ";
while(input != "exit"){
printf("Please type your command:\n");
fgets(input, 256, stdin); /* emxape: ls -alf argv[0] = "ls" argv[1] = -alf*/
printf("Input: %s", input);
i = 0;
token = strdup(strtok(input,split));
while(token != NULL){
a[i] = token;
token = strdup(strtok(NULL,split));
printf("a%d is %s\n", i, a[i]);
i++;
}
int j = 0;
while( j < sizeof(a))
{
printf("%s", a[j]);
j++;
}
//free(copy);
if(strcmp(a[0],"cd")== 0 )/*for compare pointer*/
{
if (chdir((a[1])) == 0) {
printf("Sucess change Directory.\n");
return 0;
}
else {
printf("Fault change Directroy\n");
perror("");
}
if (a[1] == NULL)
{
if(chdir(getenv("HOME"))<<0)
perror("cd");
return 0;
}
else
{
if(chdir(a[1]) <0)
perror("cd");
}
}
sprintf(path,"%s",a[0]);
pid = fork();
/*Child process*/
if(pid == 0){
//execl(path,a[0],a[1],NULL);
execl(a[0],a[1],a[2],NULL);
printf("Wrong child process: %s",path);
exit(0);
}
/*Parents Process*/
else {
wait(&status);
}
}//while
printf("Thank you.");
}/*main*/
There are at least 2 problems
Line 26: token = strdup(strtok(NULL,split)); should be token = strdup(strtok(input,split));
After the first modification, the loop in line 24 can run. but still does not run correctly. It seems the strtok(), and strdup() does not run correctly.

Resources