cd aint cd'ing [Archive] - C Board

PDA

View Full Version : cd aint cd'ing


pdstatha
04-11-2002, 03:44 AM
Ok i've written some code which implements the cd command. Problem is it doesn't actually seem to change the directory at all. It says it has but when u do a pwd ur still in the same directory!!! WHY???????????????


/*The following code will implement the*
*cd (change directory) command in UNIX.*/
#include <pwd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc,char **argv){
uid_t me;
struct passwd *user;
if(argc>1 && argv[1] != NULL){
if((errno = chdir(argv[1])) == ENOTDIR){
fprintf(stderr,"Can't change to %s\n",argv[1]);
return -1;
}else
printf("Changed to %s\n",argv[1]);
}else{
me = getuid();
user = getpwuid(me);
if(!user){
fprintf(stderr,"Couldn't find user %d\n",(int) me);
exit(EXIT_FAILURE);
}
if((errno = chdir(user->pw_dir)) == ENOTDIR){
fprintf(stderr,"Can't change to %s\n",user->pw_dir);
return -1;
}else
printf("Changed to %s\n",user->pw_dir);
}
return EXIT_SUCCESS;
}

Deckard
04-11-2002, 05:12 AM
Scope.

Let's say you are in your home directory (/home/pdstatha), and you execute a program. The program makes a call to chdir and you end up at /etc. Now /etc is the working directory for the application. When the application ends, its resources are reclaimed by the OS and you are returned to your shell, which is way back in /home/pdstatha.

pdstatha
04-11-2002, 05:55 AM
So is there a way for it not to do that???????

Deckard
04-11-2002, 06:17 AM
No. This is why the cd command is built into the shell, and not a stand alone program.

pdstatha
04-11-2002, 10:16 AM
Ok by your logic, If I had written a shell myself and coded cd into the shell then It should work. Well here goes, heres the shell plus cd coded into it. It still doesn't work tho!!!


#include "def.h"
#include <string.h>
#include <pwd.h>
#include <errno.h>

/*Prototypes*/
void changedir(char* dir);
void errorParser(int error,char* dir);

/*Main function used for printing an
endless loop of tsh: , it also does
a one of initialisation, then gets lines
of input and parses them to extract the commands
and their parameters*/
main (int argc,char** argv){
int i;
initcold();

for(;;){
initwarm();
if (getline()){
if (i = parse()){
if(argc > 1 && argv[1] != NULL)
execute(i,argv[1]);
else
execute(i,NULL);
}
}
}
}

initcold (void){
/*
signal(SIGINT, SIG_IGN);
signal (SIGOUIT, SIG_IGN);
*/
}

/*Initializes global vars,sets infd and outfd
file descriptors for each of the simple command
structures in the cmdlin[] array to default vals*/
initwarm(void){
int i;
backgnd = FALSE;
lineptr = line;
avptr = avline;
infile[0] = '\0';
outfile[0] = '\0';
append = FALSE;

for (i = 0; i<PIPELINE; ++i){
cmdlin[i].infd = 0;
cmdlin[i].outfd = 1;
}

for (i = 3; i<OPEN_MAX; ++i)
close(i);

printf("tsh: ");
fflush(stdout);
}

/*Takes characters from stdin and copies them into
the line[] array, until either a newline char or
MAXLINE number of chars have been entered*/
getline (void){
int i;

for (i = 0; (line[i] = getchar())!='\n' && i<MAXLINE; ++i);
if (i==MAXLINE){
fprintf(stderr, "Command line too long\n");
return(ERROR);
}
line[i+1] = '\0';
return (OKAY);
}

/*The parser must be
able to determine what is a command, what is a pipe,
what is a shell variable, what might the user expect
for command line completion etc.*/
parse (void){
int i;
/* 1 */
command(0);
/* 2 */
if (check("<"))
getname(infile);
/* 3 */
for (i = 1; i<PIPELINE; ++i)
if (check("|"))
command(i);
else
break;
/* 4 */
if (check(">")){
if (check(">"))
append = TRUE;
getname(outfile);
}
/* 5 */
if (check("&"))
backgnd = TRUE;
/* 6 */
if (check("\n"))
return(i);
else{
fprintf(stderr, "Command line syntax error\n");
return (ERROR);
}
}

/*Copies names of commands and their parameters
one word at a time from the line[] array to
the avline[] array. */
command(int i){
int j, flag, inword;
for (j = 0; j<MAXARG-1; ++j){
while (*lineptr==' ' || *lineptr=='\t')
++lineptr;
cmdlin[i].av[j] = avptr;
cmdlin[i].av[j+1] = NULL;
for (flag = 0; flag==0;){
switch (*lineptr){
case '>':
case '<':
case '|':
case '&':
case '\n':
if (inword==FALSE)
cmdlin[i].av[j] = NULL;
*avptr++ = '\0';
return;
case ' ':
case '\t':
inword = FALSE;
*avptr++ = '\0';
flag = 1;
break;
default:
inword = TRUE;
*avptr++ = *lineptr++;
break;
}
}
}
}

execute(int j,char* restricted){
int i, fd, fds[2];

/*If input redirection file specified*/
if (infile[0] !='\0')
cmdlin[0].infd = open(infile, O_RDONLY);

/*If output redirection file specified*/
if (outfile[0] !='\0')
if (append==FALSE)
cmdlin[j-1].outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
else
cmdlin[j-1].outfd = open(outfile, O_WRONLY | O_CREAT | O_APPEND, 0666);

/*If command line to be run in background*/
if (backgnd==TRUE)
signal (SIGCHLD, SIG_IGN);
else
signal (SIGCHLD, SIG_DFL);

/* 4 */
for (i = 0; i <j; ++i){
/* 5 */
if (i<j-1){
pipe (fds);
cmdlin[i].outfd = fds[1];
cmdlin[i+1].infd = fds[0];
}

/*Creates child process to run simple command i*/
if(restricted == NULL)
forkexec(&cmdlin[i],NULL);
else
forkexec(&cmdlin[i],restricted);

/* 7 */
if ((fd = cmdlin[i].infd)!=0)
close(fd);

if ((fd = cmdlin[i].outfd)!=1)
close(fd);
}

/* 8 */
if (backgnd==FALSE)
while (wait (NULL) != lastpid);
}


/*The point of forkexec() is that the parent
process (the shell) is halted until the child
process (the command entered into the shell)
terminates.*/
forkexec(struct cmd *ptr,char* restricted){
int i,pid;
/*Fork child process, store id returned*/
if (pid = fork()){
/* 2 */
if (backgnd==TRUE)
printf("%d\n", pid);
lastpid = pid;
}
else{
/* 3 */
if (ptr->infd==0 && backgnd==TRUE)
ptr->infd = open("/dev/null", O_RDONLY);

/* 4 */
if (ptr->infd!=0){
close(0);
dup(ptr->infd);
}

if (ptr->outfd!=1){
close(1);
dup(ptr->outfd);
}

/* 5 */
if (backgnd==FALSE){
signal (SIGINT, SIG_DFL);
signal (SIGQUIT, SIG_DFL);
}

/* 6 */
for (i = 3; i<OPEN_MAX; ++i)
close(i);

/*This is where commands get executed
use execv by here to execute you own
code*/
if((strcmp(ptr->av[0],"ls")) == 0){
execv("ls.exe",ptr->av);
exit(1);
}else if((strcmp(ptr->av[0],"cd")) == 0){
if(restricted == NULL){
if(ptr->av[1] != NULL){
changedir(ptr->av[1]);
exit(1);
}else{
changedir(NULL);
exit(1);
}
}else{
if((strcmp(restricted,"--restricted")) != 0){
if(ptr->av[1] != NULL){
changedir(ptr->av[1]);
exit(1);
}else{
changedir(NULL);
exit(1);
}
}else{
fprintf(stderr,"Sorry restricted shell\n");
exit(1);
}
}
}else if((strcmp(ptr->av[0],"pwd")) == 0){
execv("pwd.exe",ptr->av);
exit(1);
}else if((strcmp(ptr->av[0],"kill")) == 0){
if(restricted == NULL){
execv("kill.exe",ptr->av);
exit(1);
}else{
if((strcmp(restricted,"--restricted")) != 0){
execv("kill.exe",ptr->av);
exit(1);
}else{
fprintf(stderr,"Sorry restricted shell\n");
exit(1);
}
}
}else if((strcmp(ptr->av[0],"ps")) == 0){
if(restricted == NULL){
execv("ps.exe",ptr->av);
exit(1);
}else{
if((strcmp(restricted,"--restricted")) != 0){
execv("ps.exe",ptr->av);
exit(1);
}else{
fprintf(stderr,"Sorry restricted shell\n");
exit(1);
}
}
}else{
printf("%s\n",ptr->av[0]);
execvp(ptr->av[0],ptr->av);
exit(1);
}
}
}

check(char *ptr){
char *tptr;

while (*lineptr== ' ' )
lineptr++;

tptr = lineptr;

while (*ptr!='\0' && *ptr==*tptr){
ptr++;
tptr++;
}
if (*ptr!='\0')
return(FALSE);
else{
lineptr = tptr;
return(TRUE);
}
}

getname(char *name){
int i;

for (i = 0; i<MAXNAME; ++i){
switch (*lineptr){
case '>':
case '<':
case '|':
case '&':
case ' ':
case '\n':
case '\t':
*name = '\0';
return;
default:
*name++ = *lineptr++;
break;
}
}
*name = '\0';
}

void changedir(char* dir){
uid_t me; /*Used to find the user id of user*/
struct passwd *user; /*Holds details about users home directory*/
if(dir != NULL){ /*If an argument is supplied change dir to the argument*/
if((errno = chdir(dir)) == ENOTDIR)
fprintf(stderr,"Can't change to %s\n",dir);
else
errorParser(errno,dir);
}else{ /*If no argument is supplied change to the users home dir*/
me = getuid(); /*Get the user id of the user*/
user = getpwuid(me); /*Get the user info based on the uid "me"*/
if(!user){
fprintf(stderr,"Couldn't find user %d\n",(int) me);
exit(1);
}
if((errno = chdir(user->pw_dir)) == ENOTDIR)
fprintf(stderr,"Can't change to %s\n",user->pw_dir);
else
errorParser(errno,user->pw_dir);
}
}

/*Deals with error occurences*/
void errorParser(int error,char* dir){
switch(error){
case EACCES:
fprintf(stderr,"Permission denied to %s\n",dir);
break;
case ENOENT:
fprintf(stderr,"No entry %s\n",dir);
break;
case EFAULT:
fprintf(stderr,"Outside allocated memory space: %s\n",dir);
break;
case EINTR:
fprintf(stderr,"Signal caught during execution of %s\n",dir);
break;
case EIO:
fprintf(stderr,"IO Error\n");
break;
case ELOOP:
fprintf(stderr,"Too many symbolic links %s\n",dir);
break;
case ENAMETOOLONG:
fprintf(stderr,"Name too long: %s\n",dir);
break;
case ENOLINK:
fprintf(stderr,"No link exists %s\n",dir);
break;
case ETIMEDOUT:
fprintf(stderr,"Timed out\n");
break;
case EMULTIHOP:
fprintf(stderr,"Multi hop not allowed\n");
break;
default:
printf("Changed to %s\n",dir);
break;
}
}

Deckard
04-11-2002, 11:07 AM
Well, if we're gonna do this we might as well go all the way. Send me your def.h file so I can compile this and step through it with the debugger.

pdstatha
04-11-2002, 11:12 AM
Ok here's the code for the def.h


#include <stdio.h>
#include <limits.h>
#include <signal.h>
#include <fcntl.h>

#define TRUE 1
#define FALSE 0
#define OKAY 1
#define ERROR 0
#define MAXLINE 200 /*Max length of input line*/
#define MAXARG 20 /*Max number of args for simple command*/
#define PIPELINE 5 /*Max commands in simple pipeline*/
#define MAXNAME 100 /*Max length of i/o redirection filename*/

char line[MAXLINE+1]; /*User typed input line*/
char *lineptr; /*Pointer to current position in line[]*/
char avline[MAXLINE+1]; /*Argv strings taken from line[]*/
char *avptr; /*Pointer to current position in avline[]*/
char infile[MAXNAME+1]; /*Input redirection filename*/
char outfile[MAXNAME+1];/*Output redirection filename*/

int backgnd; /*TRUE if & ends pipeline else FALSE*/
int lastpid; /*PID of last simple command in pipeline*/
int append; /*TRUE for append redirection (>>) else FALSE*/

struct cmd{
char *av[MAXARG];
int infd;
int outfd;
} cmdlin[PIPELINE]; /*Argvs and fds one per simple command*/

Deckard
04-11-2002, 11:43 AM
You seem to have the same problem: scope.

Your application forks a child process, and the child process calls your changedir() function. This does not change the current working directory of the parent. Instead of forking to execute every command, consider handling the command locally unless it is unrecognized and then fork.

HTH,

pdstatha
04-11-2002, 12:16 PM
Ok this where my relative lack of experience starts to show, what you just said kinda made sense, unfortunately i'm not really sure how to begin doing that, do you have any suggestions. Or perhaps some code snippets to get me started?????

Deckard
04-11-2002, 04:04 PM
This should get you started:

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <unistd.h>

#define COMMAND_LINE_MAX 1024

int main( void )
{
char CommandLine[COMMAND_LINE_MAX + 1];
char *cptr;
char CurrentDirectory[PATH_MAX + 1];
int Result;
int StayAlive = 1;

while (StayAlive)
{
/* Wipe contents of CommandLine */

memset( CommandLine, 0, COMMAND_LINE_MAX + 1 );


/*
* Show a simple prompt that includes
* the current working directory.
*/

if ( !getcwd( CurrentDirectory, PATH_MAX ) )
{
perror( "getcwd()" );
break;
}

printf( "%s>", CurrentDirectory );


/*
* Get the command.
*/

if ( !fgets( CommandLine, COMMAND_LINE_MAX, stdin ) )
{
perror( "fgets()" );
break;
}

/* Strip trailing carriage return */

cptr = (char *) strchr( CommandLine, '\n' );
if ( cptr )
*cptr = 0;


/*
* We know how to do two things, and
* are NOT whitespace friendly.
*/

if ( !strncmp( CommandLine, "cd", 2 ) )
{
Result = chdir( CommandLine + 3 );
if ( Result == -1 )
perror( "shell" );
}
else if ( !strncmp( CommandLine, "exit", 4 ) )
{
StayAlive = 0;
}


/*
* Everything else we fake.
*/

else
{
Result = system( CommandLine );
if ( Result == -1 )
perror( "shell" );
}
}

/*
* If StayAlive is TRUE, we broke out due to error and
* errno should be returned.
*/

return (StayAlive ? errno : 0 );
}