execlp doenst work when im giving the arguments - c

why the first execlp doesn't work and the second one its ok.
int main(){
int fd[2];
int i;
char** arguments = (char**) malloc(5*sizeof(char*));
char *line = (char*) malloc(15*sizeof(char));
char *cfile = (char*) malloc(15*sizeof(char));
char* token;
size_t bufsize = 0;
for(i = 0;i < 5; i++){
arguments[i] = (char*) malloc(15*sizeof(char));
}
getline(&line, &bufsize, stdin);
i = 0;
token = strtok(line,TOK_DELIM);
while( token != NULL ){
(arguments)[i] = token;
strcat((arguments)[i++],"\0");
token = strtok(NULL,TOK_DELIM_1);
}
if( !fork() ){
execlp((arguments)[1],"show",(arguments)[0],NULL);
close(fd[0]); close(fd[1]);
}
close(fd[0]); close(fd[1]);
waitpid(0,NULL,0);
if( !fork() ){
printf("Executable c file:");
scanf("%s",cfile);
execlp(cfile,"show",(arguments)[0],NULL);
close(fd[0]); close(fd[1]);
}
close(fd[0]); close(fd[1]);
waitpid(0,NULL,0);
return 0;
}
As you can see now cfile and arguments are both vuriables(dynamic char array).
To run this main you have to gcc the show file writing gcc -o show show.c
Then when you run the main you have to type (pwd | ./show) pwd is the command that i want to execute and ./show the executable file that i want to use to print the output of pwd.I save what the user types on line and then i parse the arguments on a 2d dynamic array.On the first cell of arguments(dynamic 2d array) i save the command that i want to execute and at the second one the executable file that i want to run.
This is the show file that i want to use:
int main(int argc, char *argv[]){
char *arg[4];
printf("Execute commands from other file ...\n");
arg[0] = "sh";
arg[1] = "-c";
arg[2] = argv[1];
arg[3] = NULL;
execvp ("/bin/sh", arg);
return 0;
}
And you must compile it with the same name as the executable file that you are gonna give on the main programm,for example if you gonna type :pwd | ./show
on main you must compile the show file like this : gcc -o show show.c
Also im using those libs :
stdlib
stdio
unistd
string
sys/types
errno
sys/stat
fcntl
sys/wait

Related

Weird behaviour of C program in MacOS

I've been writing a shell program in C. The program is working as expected in Linux (Ubuntu 16.04) but I'm getting unexpected output in MacOS (10.14.2 Mojave).
/* A shell program.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
void input(char* argv[]);
void print_arr(char *argv[]); // For debugging
int
main(void)
{
while (1)
{
pid_t pid;
char *argv[100];
// Display shell prompt
write(1, "(ash) $ ", 8);
// Take user input
input(argv);
// print_arr(argv); // DEBUG STATEMENT
if (argv[0] != NULL)
{
// Exit if exit command is entered
if (strcmp(argv[0], "exit") == 0)
{
exit(0);
}
// Create child process
if ((pid = fork()) > 0)
{
wait(NULL);
}
else if (pid == 0)
{
// print_arr(argv); // DEBUG STATEMENT
execvp(argv[0], argv);
printf("%s: Command not found\n", argv[0]);
exit(0);
}
else
{
printf("Fork Error!\n");
}
}
}
}
/* Takes input from user and splits it in
tokens into argv. The last element in
argv will always be NULL. */
void
input(char* argv[])
{
const int BUF_SIZE = 1024;
char buf[BUF_SIZE];
int i;
buf[0] = '\0';
fgets((void*) buf, BUF_SIZE, stdin);
i = 0;
argv[i] = strtok(buf, " \n\0");
while (argv[i] != NULL)
{
argv[++i] = strtok(NULL, " \n\0");
}
}
/* Print argv for debugging */
void
print_arr(char *argv[])
{
int i = 0;
while (argv[i] != NULL)
{
printf("%d: %s\n", i, argv[i]);
++i;
}
}
In Linux:
(ash) $ ls
// files and folders are listed
In MacOS (with debug statements):
(ash) $ ls
0: p?M??
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
I don't understand that why are the contents of char* argv[] getting modified across fork()?
I've also tried it in the default clang compiler and brew's gcc-4.9, the results are same.
When a program behaves different for no good reason, that's a VERY good sign of undefined behavior. And it is also the reason here.
The array buf is local to the function input and ceases to exist when the function exits.
One way of solving this is to declare buf in main and pass it to input. You will also need the size of the buffer for fgets.
void
input(char * argv[], char * buf, size_t size)
{
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
Another solution (although I suspect many will frown upon it) is to declare buf as static, but then you would need to change BUF_SIZE to a #define or a hard coded value, since you cannot have a static VLA.
#define BUF_SIZE 1024
void
input(char * argv[])
{
static char buf[BUF_SIZE];
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
I removed the cast to void* since it's completely unnecessary. I also changed the while loop to a for loop to make the loop variable local to the loop.

issues while writing a shell in c

I'm trying to write a shell program in c.
The program needs to have multiple processes created by fork function and be able to print multiple output line in one command.
For example, like linux terminal, if input is "ls ; ps ; pwd ;", the output should be like this.
$./shell
shell> ls ; ps ; pwd ;
(ls output)
(ps output)
(pwd output)
And it should be able to open a file and display the command list and the output that file contains.(batch mode I guess?)
Let's say these command lists are in the batch file.
batch
1 ls
2 ps
3 ls ; pwd ; ps
And the output is
$./shell batch
shell> ls
shell> (ls output)
shell> ps
shell> (ps output)
shell> ls ; pwd ; ps
shell> (ls output)
(ps output)
(pwd output)
Here's the code I wrote
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
void command(char* myargs[][10], char* buffer);
int tokenizing(char* myargs[][10], char* buffer);
int main(int argc, char* argv[]) {
int fd;
char buffer[200];
char* myargs[10][10];
char* token;
if(argc >= 2) {
if((fd=open(argv[1], O_RDONLY)) == -1)
printf("cannot open file\n");
else {
read(fd, buffer, 200);
printf("%s\n", buffer);
token = strtok(buffer, "\n");
while(token != NULL) {
printf("%s\n", token);
command(myargs, token);
token = strtok(NULL, "\n");
}
return 0;
}
}
while(1) {
printf("prompt> ");
if(fgets(buffer, 200, stdin) == NULL||
strcmp(buffer, "quit\n") == 0)
break;
command(myargs, buffer);
}
return 0;
}
void command(char* myargs[][10], char* buffer) {
int rc = fork();
if(rc < 0) {
fprintf(stderr, "fork failed\n");
} else if(rc == 0) {
int n = tokenizing(myargs, buffer);
for(int i = 0 ; i < n; i++) {
int rc2 = fork();
if(rc2 < 0) {
fprintf(stderr, "for failed\n");
} else if(rc2 == 0) {
execvp(myargs[i][0], myargs[i]);
printf("%s: command not found\n", myargs[i][0]);
exit(0);
} else {
wait(NULL);
}
}
exit(0);
}
else {
wait(NULL);
}
}
int tokenizing(char* myargs[][10], char* buffer) {
int i = 0;
int j = 0;
int k = 0;
char* token;
char* subCommand[10];
token = strtok(buffer, ";\n");
while(token != NULL) {
subCommand[k] = token;
k++;
token = strtok(NULL, ";\n");
}
for(int i = 0; i < k; i++) {
token = strtok(subCommand[i], " \n");
while(token != NULL) {
myargs[i][j] = token;
j++;
token = strtok(NULL, " \n");
}
myargs[i][j] = NULL;
j=0;
}
}
This code works fine but has some issues. When this code runs with a batch file, I struggle with some errors.
When the program is executed, I think the output should be like the above image file - as far as I know.
But frequently the program comes out with some weird command line that I didn't even type. These results just happen alternatively.
In addition, if you see 'ps' lists, you can see two shell programs are running.
Can you guys please help me solve these problems?
'strings' in C need a NUL terminator. Calling strng functions like printf("%s....") and strtok() on char arrays that are not securely NUL-terminated results in undefined behaviour.
read() returns a value. You can use it to load a terminator into 'buffer'. To be super-safe, you should attempt to read only [buffer size -1] chars to ensure that there will always be enough space for the terminator, eg:
buffer[read(fd, buffer, 199)]='\0';

Get grep value using execl

I'm trying to make a program which invokes the ls and grep system calls using exec. Specifically I have to execute ls > tmp; grep ­-c pattern < tmp in order to count the number of files that fulfill the pattern. As you can see I save the content of ls in tmp file and then I want to use grep to count the files.
Let's supose pattern = txt. I'm trying things like the following code:
char *a = "ls > tmp";
char *b = " -c ";
char *fin = " < tmp";
char *comanda;
if((comanda = malloc(strlen(pattern)+strlen(pattern)+1)) != NULL){
comanda[0] = '\0'; // ensures the memory is an empty string
strcat(comanda,b);
strcat(comanda, pattern);
strcat(comanda,fin);
} else {
return -1;
}
ret = execl("/bin/sh","sh","-c",a,NULL);
ret = execl("/bin/sh","sh","-c",comanda, NULL);
But it shows me the following error: ls: cannot access > tmp: No such file or directory. So I don't know how to get the value of grep, because the execl function does not return the value, so how can I achieve the grep value?
To get the output of a command, you need to use a pipe.
Have a look at : Connecting n commands with pipes in a shell?
You could just do:
ls | grep -c pattern
If you just want to get files with a specific pattern in filename you might want to use find
find your_path/ -name "*pattern*" | wc -l
Have a look at Grabbing output from exec to get the output of execl
Here is an example, replace the 4th argument of execl with whatever you want :)
(execl("/bin/sh", "sh", "-c", "ls > tmp; grep -c 'pattern' < tmp", (char *)NULL);)
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2];
pipe(fd);
if (fork() == 0)
{
close(fd[0]);
dup2(fd[1], 1);
dup2(fd[1], 2);
close(fd[1]);
execl("/bin/sh", "sh", "-c", "find your_path -name '*pattern*' | wc -l", (char *)NULL);
}
else
{
char buffer[1024] = {0};
close(fd[1]);
while (read(fd[0], buffer, sizeof(buffer)) != 0)
{
write(1, buffer, strlen(buffer));
memset (buffer, 0, sizeof(buffer));
}
}
return 0;
}
You're not allocating the correct amount of space for comanda, because you don't add the sizes of all the variables correctly. So if the size is too small, you'll write outside the array bounds when you do all the strcat, and this will cause undefined behavior.
You don't need the temporary file, you can just pipe from ls to grep. I've also added quotes around the pattern in case it contains special characters.
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
char *a = "ls | grep -c '";
char *fin = "'";
char *pattern = "foo";
char *comanda;
if((comanda = malloc(strlen(a) + strlen(pattern) + strlen(fin) +1)) != NULL){
strcpy(comanda,a);
strcat(comanda,pattern);
strcat(comanda,fin);
} else {
return -1;
}
int ret = execl("/bin/sh","sh","-c", comanda, (char*)NULL);
perror("execl"); // Can only get here if there's an error
}

Redirecting execvp path

I'm trying to write a simple code which execute a program from subfolders from a input file and print thr result into a output file.
My problem is that when i execute the program it keeps failing on me. since the execvp command is trying to look for an exe named "a.out" on the wrong location. in (desktop rather than searching the correct path address).
here's the code. please help me out :)
pid_t runner;
char enter[] = "/home/demo/Desktop/OS/Ex1/Ex12/code/input.txt"; // input file
char path[] = "/home/demo/Desktop/OS/Ex1/Ex12/Ex1/ronen/"; correct path
char *r [] = {"./a.out", NULL};
int savedFD = dup(0);
int sever2Fd=dup(1);
int fdin = open(enter,O_RDONLY);
int fdout = open ("output.txt", O_CREAT | O_RDWR, 0466);
dup2(fdin, 0);
dup2(fdout, 1);
if ((runner = fork()) < 0) {perror("could not make fork");}
else if (runner == 0) {
if (execvp(r[0],r) < 0 ) {printf("Failed!\n");}
} else if (runner != 0) {
waitpid(runner,0,0);
dup2(savedFD, 0);
dup2(sever2Fd, 1);
printf("done\n");
}
close(fdin);close(fdout);
The answer was simple.
"chdir(wanted path)"
int dirchange = chdir(argv[1]);

Why is my buffer not concatenating?

If no args to main then my program should do printenv | sort | less and I've achieved that functionality. If main has arguments then the program should do printenv | grep <parameter list> | sort | less and my problem is that debugging is not working. I can try statement printf in my code and it doesn't do anything. Why? And why is the latter part of my requirement not working? What is wrong with the program?
The expected output is printenv | grep <parameter list> | sort | less. For example I would like to query the environment variables so that executing a.out JOBS COMPIZ UPSTART should be doing the same as a printenv | grep -e 'JOBS\|COMPIZ\|UPSTART' | sort | less.
Instead I get unexpected output when trying to fork a chain of commands.
#include <sys/types.h> /* definierar bland annat typen pid_t */
#include <errno.h> /* definierar felkontrollvariabeln errno */
#include <stdio.h> /* definierar stderr, dit felmeddelanden skrivs */
#include <stdlib.h> /* definierar bland annat exit() */
#include <unistd.h> /* definierar bland annat fork() */
struct command
{
const char **argv;
};
int
spawn_proc (int in, int out, struct command *cmd)
{
pid_t pid;
if ((pid = fork ()) == 0)
{
if (in != 0)
{
dup2 (in, 0);
close (in);
}
if (out != 1)
{
dup2 (out, 1);
close (out);
}
return execvp (cmd->argv [0], (char * const *)cmd->argv);
}
return pid;
}
int
fork_pipes (int n, struct command *cmd)
{
int i;
pid_t pid;
int in, fd [2];
/* The first process should get its input from the original file descriptor 0. */
in = 0;
/* Note the loop bound, we spawn here all, but the last stage of the pipeline. */
for (i = 0; i < n - 1; ++i)
{
pipe (fd);
/* f [1] is the write end of the pipe, we carry `in` from the prev iteration. */
spawn_proc (in, fd [1], cmd + i);
/* No need for the write and of the pipe, the child will write here. */
close (fd [1]);
/* Keep the read end of the pipe, the next child will read from there. */
in = fd [0];
}
/* Last stage of the pipeline - set stdin be the read end of the previous pipe
and output to the original file descriptor 1. */
if (in != 0)
dup2 (in, 0);
/* Execute the last stage with the current process. */
return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);
}
int
main (int argc, char ** argv)
{
printf("in main...");
int i;
if (argc == 1) {
const char *printenv[] = { "printenv", 0};
const char *sort[] = { "sort", 0 };
const char *less[] = { "less", 0 };
struct command cmd [] = { {printenv}, {sort}, {less} };
return fork_pipes (3, cmd);
}
if (argc > 1) {
char *tmp = argv[1];
for( i=1; i<argc-1; i++)
{
sprintf(tmp, "%s%s%s", tmp, "|", argv[i]);
}
const char *printenv[] = { "printenv", 0};
const char *grep[] = { "grep", "-E", tmp, NULL};
const char *sort[] = { "sort", 0 };
const char *less[] = { "less", 0 };
struct command cmd [] = { {printenv}, {grep}, {sort}, {less} };
return fork_pipes (4, cmd);
}
}
Part of the problem is that you are writing to a read-only memory segment by writing to argv[1] (due to the tmp = argv[1] statement). It is further aggravated by the fact that you are more than likely writing beyond the size of argv[1]. Instead you should concatenate the string to a new writable buffer of sufficient size.
To concatenate the string into the tmp variable you can use code similar to the following:
// Compute required buffer length
int len = 1; // adds 1 to the length to account for the \0 terminating char
for( i=1; i<argc; i++)
{
len += strlen(argv[i]) + 2; // +2 accounts for length of "\\|"
}
// Allocate buffer
tmp = (char*) malloc(len);
tmp[0] = '\0';
// Concatenate argument into buffer
int pos = 0;
for( i=1; i<argc; i++)
{
pos += sprintf(tmp+pos, "%s%s", (i==1?"":"|"), argv[i]);
}
printf("tmp:%s", tmp);
fflush(stdout); // force string to be printed
...
free(tmp);
As far as why the output does not appear, it is most likely due to the fact that printf is line buffered. In other words, it typically won't be printed until an end-of-line (\n) has to be printed or a fflush explicitly forces the buffer to be printed to the console.
Note: don't forget to free() the variable tmp once you are done with it.

Resources