1

I run my shell and it prompts: "Shell>". I type a command, like ls, and it just makes a new line that says "Shell>" again.

Any idea why it doesn't seem to be hitting execv?

            int no_of_args = count(buffer);
            // plus one to make it NULL
            char** array_of_strings = malloc((sizeof(char*)*(no_of_args+1)));

            //  break the string up and create an array of pointers that
            // point to each of the arguments.
            int count=0;
            char* pch2;
            pch2 = strtok (buffer," ");
            while (pch2 != NULL)
            {
                array_of_strings[count]=(char*)malloc((sizeof(char)*strlen(pch2)));
                strcpy(array_of_strings[count], pch2);

                pch2 = strtok (NULL, " ");
                count++;
            }

            //format for command is eg. ls -a -l
            //therefore the first element in the array will be the program name
            //add the path so it'll be /bin/command eg. /bin/ls
            char* prog = malloc(sizeof(char)*(strlen(array_of_strings[0]+strlen(path))));
            prog = strcat(strcpy(prog, path),array_of_strings[0]);


}
user1687558
  • 83
  • 1
  • 7

3 Answers3

3

First things first, you never need to use sizeof(char) since it's always 1.

ISO C99 defines byte thus:

addressable unit of data storage large enough to hold any member of the basic character set of the execution environment.

and later states in 6.5.3.4 The sizeof operator:

When applied to an operand that has type char, unsigned char, or signed char, (or a qualified version thereof) the result is 1.

This is unchanged in C11.

So, basically, a byte is the size of your char. ISO generally reserves the term octet for an 8-bit value.


Secondly, a statement sequence like:

array_of_strings[count]=(char*)malloc((sizeof(char)*strlen(pch2)));
strcpy(array_of_strings[count], pch2);

is undefined behaviour since strlen(pch2) is just one short of being enough space to store a copy of the string pointed to by pch2. You should be using something like:

array_of_strings[count] = malloc (strlen (pch2) + 1);

You'll also notice I removed the cast. You should never cast the return value of memory allocation functions in C since it can hide problems under some circumstances.

Thirdly, you don't seem to be following the rules with your argv array. The last element in this array should be a NULL pointer, as in the command ls x.txt would generate:

  • "ls".
  • "x.txt".
  • NULL.

Now, on to your specific problem. You should be checking the return value from your execv call since there's no guarantee that the executable will run (for example, if ls were not in the /bin directory). I would change:

int rv = execv(prog, array_of_strings);

into:

printf ("DEBUG: [%s]\n", prog);
int rv = execv(prog, array_of_strings);
printf ("DEBUG: execv returned %d/%d\n", rv, errno); // need errno.h

for debugging purposes, and see what that outputs.

If the execv works, you'll never see that final message. If it appears, it will tell you why the execv didn't work. When I do that, I see:

DEBUG: [/bin/ls
]
DEBUG: execv returned -1/2

In other words, the executable name that you're trying to run is /bin/lsX, where X is the newline character. There's no such executable, hence the error 2 (ENOENT = No such file or directory) from execv - you need to fix up the parsing code so that the newline is not left in.

As a quick debug fix, I changed the line:

prog = strcat(strcpy(prog, path),array_of_strings[0]);

into:

prog = strcat(strcpy(prog, path),array_of_strings[0]);
if (prog[strlen(prog)-1] == '\n') prog[strlen(prog)-1] = '\0';

to get rid of the trailing newline if it's there, and the file listing was then successful:

Shell>ls
DEBUG: [/bin/ls]
accounts2011.ods  birthdays    shares    workspace_android
accounts2012.ods  development  wildlife
Shell>_

That's just a debugging thing for proof, unsuitable for real code, so you still have to go and fix your parsing.


You might want to have a look at this answer as it shows a good way to get input from the user with buffer overflow protection, cleaning up the rest of the input line if too long, prompting and (most important in this case) removal of the newline.

Community
  • 1
  • 1
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • To be fair, it's probably good coding practice to use sizeof(char) in case other people have no clue what you're doing. I mean, what's the overhead on that :) – 1'' Sep 21 '12 at 01:53
  • sizeof(char) is not strictly always equal to 1, there is some exotic hardware out there which simple does not handle 1 byte words alignment, (super computers etc), and so they will use 16bit or even 32bit chars, the c standard allows for this. Also by using sizeof(char) if you decide to change you code to use unichar perhaps, then you are less likely to forget to change your sizeof(char) == 1 assumption. – Nathan Day Sep 21 '12 at 01:54
  • 3
    @Nathan, there is **no** ISO-compliant C compiler where `sizeof(char) != 1`. ISO defines a byte not as 8 bits (an octet) but as the size of the basic storage unit, a char. 1024-bit bytes are possible but that's still a byte and there is _one_ byte per char (the char would be 1024 bits wide ie. one byte). And, if you want to handle expansion to other types, you don't use the type (since you have to change that anyway if the type changes). You use the varaible: `biggerchar *xyz = malloc (sizeof (*xyz));`. – paxdiablo Sep 21 '12 at 02:01
  • 2
    @NathanDay You're wrong; the C standard mandates that sizeof(char) == 1. – Jim Balter Sep 21 '12 at 02:05
  • @user1397061 To be fair, it is poor practice to use sizeof(char), as it is just noise, it is inconsistent with millions of existing lines of code, and it could mislead naive programmers into thinking that it's needed. – Jim Balter Sep 21 '12 at 02:07
  • 'You should never cast the return value of memory allocation functions in C since it can cause problems under some circumstances' ... it doesn't cause problems, but it can **hide** a problem if you forgot to include stdlib.h, resulting in the compiler thinking that malloc returns an int, – Jim Balter Sep 21 '12 at 02:08
  • That sounds like semantics, @Jim :-) But you're right. The root cause of the problem is always there and the cast stops a compiler from reporting it. So I'll defer to you there and update. – paxdiablo Sep 21 '12 at 02:15
  • @paxdiablo semantics = meaning. As a programmer, there's not much that's more important than semantics. – Jim Balter Sep 21 '12 at 02:21
  • No the C standard says that a char just has to be a least 8bit. http://stackoverflow.com/questions/881894/is-char-guaranteed-to-be-exactly-8-bit-long-in-c http://flash-gordon.me.uk/ansi.c.txt – Nathan Day Sep 21 '12 at 03:59
  • @Nathan, I'm not sure what you're trying to say. Yes, a char has to be >= 8 bits, I didn't dispute that. I disputed your definition of byte. ISO reserves the term octet for an 8-bit value, byte in the C standard is not 8 bits, it's defined as the size needed to hold a char. Ergo sizeof(char) is _always_ 1. – paxdiablo Sep 21 '12 at 04:06
  • According to the wiki page a char just has to the smallest addressable unit, there are super computers which can not address on byte alignment, they still have bytes = 8bits, addresses just have to be multiples of 2 or 4. – Nathan Day Sep 21 '12 at 04:13
  • I just saw this http://stackoverflow.com/questions/2215445/are-there-machines-where-sizeofchar-1 looks like you are right. – Nathan Day Sep 21 '12 at 04:15
  • Thanks so much for your help! The execv debugging is really helpful. So I got it to execute "ls" and "ls -l", but it can't do any more arguments than that: ls -l -a DEBUG: [/bin/ls]' DEBUG: execv returned -1/14 Shell> What does -1/14 mean? Thank you again – user1687558 Sep 21 '12 at 15:41
  • Scratch that last comment - my counting function is stupid. 'ls -a -l' has two spaces, so it returns 2 arguments when it should return 3. Thank you all for the help – user1687558 Sep 21 '12 at 15:47
2

The line

char* prog = malloc(sizeof(char)*(strlen(array_of_strings[0]+strlen(path))));

seems wrong. Are you sure you don't mean

char* prog = malloc(sizeof(char)*(strlen(array_of_strings[0])+strlen(path)));

(note moved parenthesis). strlen(array_of_strings[0]+strlen(path)) (the number of bytes you're reserving) will bear an unpredictable relationship to the sum of the lengths of array_of_strings[0] and path (the strings you're trying to concatenate). This may be causing a segfault.

Kevin
  • 53,822
  • 15
  • 101
  • 132
0

There is also something weird happening with the current working directory after the execv. Try "Shell>pwd", or even "Shell>ls /home/".

Anyway, I think I might have solved it by removing the '\n' character at the end of the "buffer" string, right after the fgets. See if it works for you:

 fgets(buffer, 512, stdin);
 int j = strlen(buffer) - 1;
 if (buffer[j] == '\n')
     buffer[j] = 0;

Still a mystery to me why the weird CWD behaviour happened...

Hope this helps.