I'm hoping someone here could help me cause I'm witnessing some weird behavior from my C code.
I have a Linux shell-like program in C. The code is 250 lines long, so I can't be posting it here.
In short, I have this function for non-built in commands:
int not_builtin(char** args, int background, char* line) {
int status;
pid_t pid;
pid = fork();
if (pid < 0) {
printf("fork failed\n");
}
else if (pid == 0) {
if (execvp(args[0], args) == -1) {
printf("exec failed\n");
}
return 0;
}
else {
if (!background) {
if (waitpid(pid, &status, 0) < 0) {
printf("An error occurred\n");
}
}
add_job(line, pid, background);
}
return 1;
}
When I run my shell on my PC (Macbook), everything works as expected. BUT, when I run this on my university server (Linux), I get a bug. I cannot properly debug this since on my computer everything works fine. The bug is it outputs: "exec failed" for any command (line 9).
By printing through the code, I was able to detect the following behavior:
The char** args are good throughout the code, I printed them until just before the "if (pid == 0)".
Once you enter the if statement mentioned above, printing the args returns some garbage.
Specifically, for an input: $ ls -l
I got printed ls -l before the if, and ?\?? after the if.
Like this for any other command string.
What could be the reason?
The whole day the program worked fine. It started to happen once I changed my "parse-line" function, to copy the line before editing. It now looks like this:
char** parse_line(char *line, int *background) {
char line_copy[BUFF_SIZE];
strcpy(line_copy, line);
char **tokens = malloc(BUFF_SIZE*sizeof(char*));
if (!tokens) {
printf("An error occurred\n");
exit(EXIT_FAILURE);
}
int position = 0;
char *token;
token = strtok(line_copy, DELIM);
while (token) {
tokens[position] = token;
position++;
token = strtok(NULL, DELIM);
}
// change the background option
if (position > 0 && strcmp(tokens[position-1], "&")==0) {
*background = 1;
tokens[position-1]=NULL;
line[strlen(line)-2] = '\0';
} else {
*background = 0;
tokens[position] = NULL;
}
return tokens;
}
Before the change I would perform the strtok directly on line, but now I figured I need that value intact. But how is this connect in any way?
Any help would be MUCH appreciated
I want to pipe a string to a program that read input only from file, but not from stdin. Using it from bash, i can do something like
echo "hi" | program /dev/stdin
and I wanted to replicate this behaviour from C code. What I did is this
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
int main() {
pid_t pid;
int rv;
int to_ext_program_pipe[2];
int to_my_program_pipe[2];
if(pipe(to_ext_program_pipe)) {
fprintf(stderr,"Pipe error!\n");
exit(1);
}
if(pipe(to_my_program_pipe)) {
fprintf(stderr,"Pipe error!\n");
exit(1);
}
if( (pid=fork()) == -1) {
fprintf(stderr,"Fork error. Exiting.\n");
exit(1);
}
if(pid) {
close(to_my_program_pipe[1]);
close(to_ext_program_pipe[0]);
char string_to_write[] = "this is the string to write";
write(to_ext_program_pipe[1], string_to_write, strlen(string_to_write) + 1);
close(to_ext_program_pipe[1]);
wait(&rv);
if(rv != 0) {
fprintf(stderr, "%s %d\n", "phantomjs exit status ", rv);
exit(1);
}
char *string_to_read;
char ch[1];
size_t len = 0;
string_to_read = malloc(sizeof(char));
if(!string_to_read) {
fprintf(stderr, "%s\n", "Error while allocating memory");
exit(1);
}
while(read(to_my_program_pipe[0], ch, 1) == 1) {
string_to_read[len]=ch[0];
len++;
string_to_read = realloc(string_to_read, len*sizeof(char));
if(!string_to_read) {
fprintf(stderr, "%s\n", "Error while allocating memory");
}
string_to_read[len] = '\0';
}
close(to_my_program_pipe[0]);
printf("Output: %s\n", string_to_read);
free(string_to_read);
} else {
close(to_ext_program_pipe[1]);
close(to_my_program_pipe[0]);
dup2(to_ext_program_pipe[0],0);
dup2(to_my_program_pipe[1],1);
if(execlp("ext_program", "ext_program", "/dev/stdin" , NULL) == -1) {
fprintf(stderr,"execlp Error!");
exit(1);
}
close(to_ext_program_pipe[0]);
close(to_my_program_pipe[1]);
}
return 0;
}
It is not working.
EDIT
I don't get the ext_program output, that should be saved in string_to_read. The program just hangs. I can see that ext_program is executed, but I don't get anything
I would like to know if there is an error, or if what I want cannot be done. Also I know that the alternative is to use named pipes.
EDIT 2: more details
As I still can not get my program working, I post the complete code
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
int main() {
pid_t pid;
int rv;
int to_phantomjs_pipe[2];
int to_my_program_pipe[2];
if(pipe(to_phantomjs_pipe)) {
fprintf(stderr,"Pipe error!\n");
exit(1);
}
if(pipe(to_my_program_pipe)) {
fprintf(stderr,"Pipe error!\n");
exit(1);
}
if( (pid=fork()) == -1) {
fprintf(stderr,"Fork error. Exiting.\n");
exit(1);
}
if(pid) {
close(to_my_program_pipe[1]);
close(to_phantomjs_pipe[0]);
char jsToExectue[] = "var page=require(\'webpage\').create();page.onInitialized=function(){page.evaluate(function(){delete window._phantom;delete window.callPhantom;});};page.onResourceRequested=function(requestData,request){if((/http:\\/\\/.+\?\\\\.css/gi).test(requestData[\'url\'])||requestData.headers[\'Content-Type\']==\'text/css\'){request.abort();}};page.settings.loadImage=false;page.settings.userAgent=\'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36\';page.open(\'https://stackoverflow.com\',function(status){if(status!==\'success\'){phantom.exit(1);}else{console.log(page.content);phantom.exit();}});";
write(to_phantomjs_pipe[1], jsToExectue, strlen(jsToExectue) + 1);
close(to_phantomjs_pipe[1]);
int read_chars;
int BUFF=1024;
char *str;
char ch[BUFF];
size_t len = 0;
str = malloc(sizeof(char));
if(!str) {
fprintf(stderr, "%s\n", "Error while allocating memory");
exit(1);
}
str[0] = '\0';
while( (read_chars = read(to_my_program_pipe[0], ch, BUFF)) > 0)
{
len += read_chars;
str = realloc(str, (len + 1)*sizeof(char));
if(!str) {
fprintf(stderr, "%s\n", "Error while allocating memory");
}
strcat(str, ch);
str[len] = '\0';
memset(ch, '\0', BUFF*sizeof(ch[0]));
}
close(to_my_program_pipe[0]);
printf("%s\n", str);
free(str);
wait(&rv);
if(rv != 0) {
fprintf(stderr, "%s %d\n", "phantomjs exit status ", rv);
exit(1);
}
} else {
dup2(to_phantomjs_pipe[0],0);
dup2(to_my_program_pipe[1],1);
close(to_phantomjs_pipe[1]);
close(to_my_program_pipe[0]);
close(to_phantomjs_pipe[0]);
close(to_my_program_pipe[1]);
execlp("phantomjs", "phantomjs", "--ssl-protocol=TLSv1", "/dev/stdin" , (char *)NULL);
}
return 0;
}
What I am trying to do is to pass to phantomjs a script to execute through pipe and then read the resulting HTML as a string. I modified the code as told, but phantomjs still does not read from stdin.
I tested the script string by creating a dumb program that writes it to a file and then executed phantomjs normally and that works.
I also tryed to execute execlp("phantomjs", "phantomjs", "--ssl-protocol=TLSv1", "path_to_script_file" , (char *)NULL); and that works too, the output HTML is showed.
It does not work when using pipe.
An Explanation At Last
Some experimentation with PhantomJS shows that the problem is writing a null byte at the end of the JavaScript program sent to PhantomJS.
This highlights two bugs:
The program in the question sends an unnecessary null byte.
PhantomJS 2.1.1 (on a Mac running macOS High Sierra 10.13.3) hangs when an otherwise valid program is followed by a null byte
The code in the question contains:
write(to_phantomjs_pipe[1], jsToExectue, strlen(jsToExectue) + 1);
The + 1 means that the null byte terminating the string is also written to phantomjs. And writing that null byte causes phantomjs to hang. That is tantamount to a bug — it certainly isn't clear why PhantomJS hangs without detecting EOF (there is no more data to come), and without giving an error, etc.
Change that line to:
write(to_phantomjs_pipe[1], jsToExectue, strlen(jsToExectue));
and the code works as expected — at least with PhantomJS 2.1.1 on a Mac running macOS High Sierra 10.13.3.
Initial analysis
You aren't closing enough file descriptors in the child.
Rule of thumb: If you
dup2()
one end of a pipe to standard input or standard output, close both of the
original file descriptors returned by
pipe()
as soon as possible.
In particular, you should close them before using any of the
exec*()
family of functions.
The rule also applies if you duplicate the descriptors with either
dup()
or
fcntl()
with F_DUPFD
The child code shown is:
} else {
close(to_ext_program_pipe[1]);
close(to_my_program_pipe[0]);
dup2(to_ext_program_pipe[0],0);
dup2(to_my_program_pipe[1],1);
if(execlp("ext_program", "ext_program", "/dev/stdin" , NULL) == -1) {
fprintf(stderr,"execlp Error!");
exit(1);
}
close(to_ext_program_pipe[0]);
close(to_my_program_pipe[1]);
}
The last two close() statements are never executed; they need to appear before the execlp().
What you need is:
} else {
dup2(to_ext_program_pipe[0], 0);
dup2(to_my_program_pipe[1], 1);
close(to_ext_program_pipe[0]);
close(to_ext_program_pipe[1]);
close(to_my_program_pipe[0]);
close(to_my_program_pipe[1]);
execlp("ext_program", "ext_program", "/dev/stdin" , NULL);
fprintf(stderr, "execlp Error!\n");
exit(1);
}
You can resequence it splitting the close() calls, but it is probably better to regroup them as shown.
Note that there is no need to test whether execlp() failed. If it returns, it failed. If it succeeds, it does not return.
There could be another problem. The parent process waits for the child to exit before reading anything from the child. However, if the child tries to write more data than will fit in the pipe, the process will hang, waiting for some process (which will have to be the parent) to read the pipe. Since they're both waiting for the other to do something before they will do what the other is waiting for, it is (or, at least, could be) a deadlock.
You should also revise the parent process to do the reading before the waiting.
if (pid) {
close(to_my_program_pipe[1]);
close(to_ext_program_pipe[0]);
char string_to_write[] = "this is the string to write";
write(to_ext_program_pipe[1], string_to_write, strlen(string_to_write) + 1);
close(to_ext_program_pipe[1]);
char *string_to_read;
char ch[1];
size_t len = 0;
string_to_read = malloc(sizeof(char));
if(!string_to_read) {
fprintf(stderr, "%s\n", "Error while allocating memory");
exit(1);
}
while (read(to_my_program_pipe[0], ch, 1) == 1) {
string_to_read[len] = ch[0];
len++;
string_to_read = realloc(string_to_read, len*sizeof(char));
if (!string_to_read) {
fprintf(stderr, "%s\n", "Error while allocating memory\n");
exit(1);
}
string_to_read[len] = '\0';
}
close(to_my_program_pipe[0]);
printf("Output: %s\n", string_to_read);
free(string_to_read);
wait(&rv);
if (rv != 0) {
fprintf(stderr, "%s %d\n", "phantomjs exit status ", rv);
exit(1);
}
} …
I'd also rewrite the code to read in big chunks (1024 bytes or more). Just don't copy more data than the read returns, that's all. Repeatedly using realloc() to allocate one more byte to the buffer is ultimately excruciatingly slow. It won't matter much if there's only a few bytes of data; it will matter if there are kilobytes or more data to process.
Later: Since the PhantomJS program generates over 90 KiB of data in response to the message it was sent, this was a factor in the problems — or would have been were it not for the hang-on-null-byte bug in PhantomJS.
Still having problems 2018-02-03
I extracted the code, as amended, into a program (pipe89.c, compiled to pipe89). I got inconsistent crashes when the space allocated changed. I eventually realized that you're reallocating one byte too little space — it took a lot longer than it should have done (but it would help if Valgrind was available for macOS High Sierra — it isn't yet).
Here's the fixed code with debugging information commented output:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
int rv;
int to_ext_program_pipe[2];
int to_my_program_pipe[2];
if (pipe(to_ext_program_pipe))
{
fprintf(stderr, "Pipe error!\n");
exit(1);
}
if (pipe(to_my_program_pipe))
{
fprintf(stderr, "Pipe error!\n");
exit(1);
}
if ((pid = fork()) == -1)
{
fprintf(stderr, "Fork error. Exiting.\n");
exit(1);
}
if (pid)
{
close(to_my_program_pipe[1]);
close(to_ext_program_pipe[0]);
char string_to_write[] = "this is the string to write";
write(to_ext_program_pipe[1], string_to_write, sizeof(string_to_write) - 1);
close(to_ext_program_pipe[1]);
char ch[1];
size_t len = 0;
char *string_to_read = malloc(sizeof(char));
if (string_to_read == 0)
{
fprintf(stderr, "%s\n", "Error while allocating memory");
exit(1);
}
string_to_read[len] = '\0';
while (read(to_my_program_pipe[0], ch, 1) == 1)
{
//fprintf(stderr, "%3zu: got %3d [%c]\n", len, ch[0], ch[0]); fflush(stderr);
string_to_read[len++] = ch[0];
char *new_space = realloc(string_to_read, len + 1); // KEY CHANGE is " + 1"
//if (new_space != string_to_read)
// fprintf(stderr, "Move: len %zu old %p vs new %p\n", len, (void *)string_to_read, (void *)new_space);
if (new_space == 0)
{
fprintf(stderr, "Error while allocating %zu bytes memory\n", len);
exit(1);
}
string_to_read = new_space;
string_to_read[len] = '\0';
}
close(to_my_program_pipe[0]);
printf("Output: %zu (%zu) [%s]\n", len, strlen(string_to_read), string_to_read);
free(string_to_read);
wait(&rv);
if (rv != 0)
{
fprintf(stderr, "%s %d\n", "phantomjs exit status ", rv);
exit(1);
}
}
else
{
dup2(to_ext_program_pipe[0], 0);
dup2(to_my_program_pipe[1], 1);
close(to_ext_program_pipe[0]);
close(to_ext_program_pipe[1]);
close(to_my_program_pipe[0]);
close(to_my_program_pipe[1]);
execlp("ext_program", "ext_program", "/dev/stdin", NULL);
fprintf(stderr, "execlp Error!\n");
exit(1);
}
return 0;
}
It was tested on a program which wrote 5590 byte out for 27 bytes of input. That isn't as massive a multiplier as in your program, but it proves a point.
I still think you'd do better not reallocating a single extra byte at a time — the scanning loop should use a buffer of, say, 1 KiB and read up to 1 KiB at a time, and allocate the extra space all at once. That's a much less intensive workout for the memory allocation system.
Problems continuing on 2018-02-05
Taking the code from the Edit 2 and changing only the function definition from int main() { to int main(void) { (because the compilation options I use don't allow old-style non-prototype function declarations or definitions, and without the void, that is not a prototype), the code is
working fine for me. I created a surrogate phantomjs program (from another I already have lying around), like this:
#include <stdio.h>
int main(int argc, char **argv, char **envp)
{
for (int i = 0; i < argc; i++)
printf("argv[%d] = <<%s>>\n", i, argv[i]);
for (int i = 0; envp[i] != 0; i++)
printf("envp[%d] = <<%s>>\n", i, envp[i]);
FILE *fp = fopen(argv[argc - 1], "r");
if (fp != 0)
{
int c;
while ((c = getc(fp)) != EOF)
putchar(c);
fclose(fp);
}
else
fprintf(stderr, "%s: failed to open file %s for reading\n",
argv[0], argv[argc-1]);
return(0);
}
This code echoes the argument list, the environment, and then opens the file named as the last argument and copies that to standard output. (It is highly specialized because of the special treatment for argv[argc-1], but the code before that is occasionally useful for debugging complex shell scripts.)
When I run your program with this 'phantomjs', I get the output I'd expect:
argv[0] = <<phantomjs>>
argv[1] = <<--ssl-protocol=TLSv1>>
argv[2] = <</dev/stdin>>
envp[0] = <<MANPATH=/Users/jleffler/man:/Users/jleffler/share/man:/Users/jleffler/oss/share/man:/Users/jleffler/oss/rcs/man:/usr/local/mysql/man:/opt/gcc/v7.3.0/share/man:/Users/jleffler/perl/v5.24.0/man:/usr/local/man:/usr/local/share/man:/usr/share/man:/opt/gnu/share/man>>
envp[1] = <<IXH=/opt/informix/12.10.FC6/etc/sqlhosts>>
…
envp[49] = <<HISTFILE=/Users/jleffler/.bash.jleffler>>
envp[50] = <<_=./pipe31>>
var page=require('webpage').create();page.onInitialized=function(){page.evaluate(function(){delete window._phantom;delete window.callPhantom;});};page.onResourceRequested=function(requestData,request){if((/http:\/\/.+?\\.css/gi).test(requestData['url'])||requestData.headers['Content-Type']=='text/css'){request.abort();}};page.settings.loadImage=false;page.settings.userAgent='Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36';page.open('https://stackoverflow.com',function(status){if(status!=='success'){phantom.exit(1);}else{console.log(page.content);phantom.exit();}});
At this point, I have to point the finger at phantomjs in your environment; it doesn't seem to behave as expected when you do the equivalent of:
echo "$JS_PROG" | phantomjs /dev/stdin | cat
Certainly, I cannot reproduce your problem any more.
You should take my surrogate phantomjs code and use that instead of the real phantomjs and see what you get.
If you get output analogous to what I showed, then the problem is with the real phantomjs.
If you don't get output analogous to what I showed, then maybe there is a problem with your code from the update to the question.
Later: Note that because the printf() uses %s to print the data, it would not notice the extraneous null byte being sent to the child.
In the pipe(7) man it is written that you should read from pipe ASAP:
If a process attempts to write to a
full pipe (see below), then write(2) blocks until sufficient data has
been read from the pipe to allow the write to complete. Nonblocking
I/O is possible by using the fcntl(2) F_SETFL operation to enable the
O_NONBLOCK open file status flag.
and
A pipe has a limited capacity. If the pipe is full, then a write(2)
will block or fail, depending on whether the O_NONBLOCK flag is set
(see below). Different implementations have different limits for the
pipe capacity. Applications should not rely on a particular
capacity: an application should be designed so that a reading process
consumes data as soon as it is available, so that a writing process
does not remain blocked.
In your code you write, wait and only then read
write(to_ext_program_pipe[1], string_to_write, strlen(string_to_write) + 1);
close(to_ext_program_pipe[1]);
wait(&rv);
//...
while(read(to_my_program_pipe[0], ch, 1) == 1) {
//...
Maybe the pipe is full or ext_program is waiting for the data to be read, you should wait() only after the read.
I'm not very experienced in C programming and I'm trying to write a program using pipes. The thing is that when I try to compile it, it gives me a strange error I can't understand, even by reading other similar questions in Stackoverflow.
The errors are the next ones:
ejercicio.c:91:8: error: called object type 'ssize_t' (aka 'long') is not a function or function pointer
read(tuberiaCom[0], login, sizeof(int));
~~~~^
ejercicio.c:103:8: error: called object type 'ssize_t' (aka 'long') is not a function or function pointer
read(tuberiaCom[0], *checkuser, 256);
And the affected code is this one:
int main(){
int tuberiaCom[2];
int fduser;
int login = 0;
char username[256];
char answer[1024];
char checkuser[256];
char command[64];
char program[16];
char options[48];
char *line = NULL;
pid_t familia;
size_t len = 0;
ssize_t read;
if(pipe(tuberiaCom) == -1){
fprintf(stderr, "Pipe error\n");
exit(1);
}
switch(familia = fork()){
case -1:
fprintf(stderr, "Fork error\n");
exit(1);
break;
case 0:
scanf("Please, write your username %s", username);
write(tuberiaCom[1], &username, strlen(username));
read(tuberiaCom[0], login, sizeof(int)); /////////Line 91
if(login == 1){
printf("User %s exists, you can continue", username);
}
else{
printf("There is no user called %s, the program will finnish", username);
exit(1);
}
break;
default:
read(tuberiaCom[0], *checkuser, 256); /////////Line 103
fduser = open("users.txt", O_RDONLY);
while ((read = getline(&line, &len, fduser)) != -1){
if(*line == *checkuser){
login = 1;
break;
}
}
write(tuberiaCom[1], &login, sizeof(int));
break;
}
......
You have a variable named read which is confusing compiler with the function read(2) when you call read(2). Rename your variable read to something else.
I am trying to define a global pointer variable that can then truly be set in the main function as seen below. However I am getting a segmentation fault anytime I try to use outputName after this. I know it probably has to do with setting the pointer equal to NULL at the beginning... any help on how I could have a global pointer that is then set in main would be very helpful! Here is the part of my code that is giving me errors:
char* outputName = NULL;
int isNumber(char number[]){
int i;
if (number[0] == '-')
i = 1;
while(number[i] != '\0'){
if (!isdigit(number[i]))
return 0;
i++;
}
return 1;
}
void catcher(int signo){
printf("The program is exiting early");
remove(outputName);
exit(1);
}
int main(int argc, char *argv[]){
if (argc != 4){
fprintf(stderr,"Incorrect number of arguments, must supply three.\n");
exit(1);
}
char* inputName = argv[1];
outputName = argv[2];
signal(SIGINT, catcher);
int result = isNumber(argv[3]);
if (result == 0){
fprintf(stderr, "Invalid maximum line length, please enter an integer\n");
exit(1);
}
int maxChars = (atoi(argv[3])) + 1;
if ((maxChars-1) < 1){
fprintf(stderr, "Invalid third maximum line length, please enter an integer greater than zero\
.\n");
exit(1);
}
FILE* inFile = fopen(inputName, "r");
if (inFile == NULL){
fprintf(stderr, "Error while opening %s.\n", inputName);
exit(1);
}
FILE* outFile = fopen(outputName, "w");
if (outFile == NULL){
fprintf(stderr, "Error while opening %s.\n", outputName);
exit(1);
}
char line[maxChars];
int done = 0;
while (!done){
char *readLine = fgets(line, maxChars, inFile);
if (readLine == NULL){
if (errno == 0){
done = 1;
} else {
fprintf(stderr, "Error when reading line from input file");
exit(1);
}
}
int len = strlen(line);
if (line[len-1] != '\n'){
line[len] = '\n';
line[len+1] = '\0';
char current = ' ';
while (current != '\n')
current = getc(inFile);
}
if (!done){
fputs(line, outFile);
if (errno != 0){
fprintf(stderr, "Error when writing line to output file");
exit(1);
}
}
}
return 0;
}
May be signal handler is getting called prior to outputName getting set to non null value, you can try setting signal handler after outputName = argv[2]; in main()
Read carefully signal(7): since your catcher calls printf which is not an async signal safe function, your code has undefined behavior. Also, your printf control string don't end with \n, and since stdout is line-buffered, no output would be done. Prefer sigaction(2) to signal, and install your signal handler after having assigned outputName.
Global variables used in signal handlers should be declared volatile. So declare your char* volatile outputName; at global scope. Then you might have a test like if (outputName != NULL) remove(outputName); in the handler. A common practice is just to set some volatile sig_atomic_t global flag in the signal handler, and test that flag elsewhere.
And your program is likely to not have time to get any signal. You probably should end your main function with some wait (e.g. read from stdin, or usleep(3), or pause(2), or poll(2)....).
Of course, compile your code with all warnings and debug info (gcc -Wall -g) and use the debugger (gdb); I guess that debugger watchpoints should be very useful to find your bug.
The program you are showing is likely to not exhibit any SEGV. So your actual bug is very probably elsewhere.
Perhaps using strace(1) and/or valgrind could also help.
I have the following problem: In my code, here in line 83, I have this: check = wait(NULL);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include <sys/stat.h>
#include <sys/types.h>
//---------------------------------
//Function: parse_cmdline(const char* cmdline)
//This function takes the input from stdin
//and returns it as array of strings.
//If stdin is /bin/ls -l /usr/include
//the function will return ["/bin/ls","-l","/usr/include"]
//---------------------------------
char** parse_cmdline(const char* cmdline) {
int count, word_count = 0;
char** line_parsed, line_return;
char *pch, *cmdline_copy = (char*)malloc(sizeof(char)*(strlen(cmdline)+1));
strcpy(cmdline_copy, cmdline);
pch = strtok(cmdline_copy," \n\t\r");
while (pch != NULL) {
++word_count;
pch = strtok(NULL, " \n\t\r");
}
line_parsed = (char**)malloc((word_count+1)*sizeof(char*));
count = 0;
strcpy(cmdline_copy, cmdline);
pch = strtok(cmdline_copy," \n\t\r");
while (pch != NULL) {
line_parsed[count] = (char*)malloc((strlen(pch) + 1)*sizeof(char));
strcpy(line_parsed[count], pch);
++count;
pch = strtok(NULL," \n\t\r");
}
line_parsed[count] = NULL;
free(cmdline_copy);
return line_parsed;
}
int main() {
int count = 0, check;
size_t size;
char* line;
char** cmdline;
while(1) {
check = 0;
printf("$Monkey Eats:< ");
getline(&line, &size, stdin);
cmdline = parse_cmdline(line);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return -1;
} else if(pid == 0) {
struct stat _stat;
stat(cmdline[0],&_stat);
if(_stat.st_mode & S_IXUSR){
execvp(cmdline[0], cmdline);
}else fprintf(stderr,"%s: Permission denied!\n",cmdline[0]);
perror("");
exit(1);
}else {
check = wait(NULL);
}
count = 0;
while(cmdline[count] != NULL) {
free(cmdline[count]);
++count;
}
free(cmdline);
}
return 0;
}
It makes me a problem. When I run it and when I type a command I have the following message:
$Monkey Eats:< ls
ls: Permission denied!
No such file or directory
If I have only wait(NULL); the program runs normally without a problem. Can somebody tell me what is the problem? Thank you :)
The problem is trying to run ls. execvp() doesn't know where ls is. Try running /bin/ls as your command.
The problem is: stat(cmdline[0],&_stat); - the return code is not checked. What if file not found ? The program continues, and finds that _stat.st_mode & S_IXUSR is 0 (randomly).
However you may test the program as is with "/bin/ls" as input..