Writing your own Unix shell in C - Problems with PATH and execv

I am writing my own shell in C. It should be able to display the current user directory, execute commands based on the full path ( should use execv ) and allow the user to change the directory to cd.

This is homework. Teacher gave us a basic C tutorial and a very short skeleton on how the program should work. Since I'm not one of those who gave up easily, I researched how to do this for three days, but now I'm at a standstill.

This is what I have so far:

  • Displays the username, username and current directory (the default is the home directory).
  • Prompts user for input and gets input
  • Splitting user input into "" into an array of arguments
  • Separates the PATH environment variable by ":" into an array of tokens

I am not sure how to proceed from here. I know that I need to use the execv command, but in my research on google I really did not find an example that I understand. For example, if the command is bin / ls, how does execv know the mapping of all files / folders from the home directory? How to tell the system that I changed the directory?

I often used this site, which was useful: http://linuxgazette.net/111/ramankutty.html , but again, I'm at a dead end.

Thank you for your help. Let me know if I should publish some of my existing codes, I was not sure if this was necessary.

+4
source share
4 answers

To implement the cd command, you need the chdir system call.

 #include <unistd.h> int chdir( const char *path /* the path name */ ); 

So you can just call something like:

 int ret1 = chdir("../foo/bar"); 

The return value of chdir is 0 when it was possible to change to this directory and -1 if an error occurred. For error you must consolidate the man page.

The current directory can be checked by any program, therefore, if you execute ls without any arguments, then ls checks which directory it is running in, and uses this directory as the only argument. This is an ls function, not an execv call.

For the second part.

 #include <unistd.h> int execv( const char *path, /* programm path*/ char *const argv[]/* argument vector*/ ); 

execv executes the executable in the given path and with the arguments given in argv . So if you want to execute /bin/ls ../foo /bar , you need something similar to

 char *cmd_str = "/bin/ls"; char *argv[] = {cmd_str, "../foo", "/bar", NULL }; if (execv(cmd_str, argv) == -1 ){ /* an error occurred */ } 

The error returned by execv is -1. If you want to know why he did not execute the command, check the manual pages.

NULL in char *argv[] = {cmd_str, "../foo", "/bar", NULL }; indicates that there are no other arguments after NULL .

The third part. A Unix-based system typically processes commands using a / in as commands that can be executed directly. This means that you first check for a slash on the given command line.

 int ret_value; if (strchr(cmd_str, '/') if (execv(cmd_str, argv) == -1 ){ /* an error occurred */ } 

If there is no slash, you need to go through all the directories in path and check if you can execute the command. Thus, this command is ls ../foo /bar and suggests that the path value is ".:/sbin:/bin:/usr/bin" . Then we tried to execute ./ls ../foo /bar , then /usr/bin/ls ../foo /bar and finally /bin/ls ../foo /bar .

Hope this helps.

+1
source

For example, if the command is bin/ls , how execv know the mapping of all files / folders from the home directory? How to tell the system that I changed the directory?

Each process has a current working directory, which can be changed using chdir . Child processes inherit the working directory from the parent. Thus, in general, your shell will manage its current working directory in response to the cd commands entered by the user. When you enter a command that is not inline, you fork to create a child process, and then call execv there to execute the binary.

If you ant took PATH into account for program names that do not contain any part of the directory, you should try all possible combinations of the PATH element and program name. You can check if the named file exists, or just try to execute it and continue with the next one if that doesn't work. If all execv calls have failed, you will need to call _exit to complete the child process.

Note that most shells will handle any command that contains / as a path that is passed directly to execv . If the path does not start with / , then this is a relative path, and the operating system will resolve it relative to the current working directory. In other words, bin/ls from your example will refer to the ls binary in the bin , which is a subdirectory of the current working directory. Only commands that do not contain any / at all are interpreted as a built-in command (for example, cd ) or the name of some binary located on PATH .

The first argument to execv is the path that you calculated it. The first element of the argv list is traditionally equal to the name that was entered, i.e. Without added PATH directory. After this first argument, any additional command line options are passed, followed by NULL to complete the list.

+1
source

I think the problem is that you think the shell is responsible for doing ls work. ls is not really a β€œpart” of the shell (in this case, at least). The environment runs a program called ls . Most of the comments seem to explain how to find ls , but I do not believe you are confused.

You must carefully consider what the point of the shell is before writing it. Comments indirectly indicate that the shell "just" should "call" programs like ls and chdir , and not perform its tasks.

+1
source

ls itself knows that if no arguments are given, it should display the files in the current working directory returned by getcwd

+1
source

Source: https://habr.com/ru/post/1436923/


All Articles