LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 07-27-2015, 10:57 AM   #1
Kslkgh
LQ Newbie
 
Registered: Jul 2015
Location: The computer
Distribution: Debian, Kali Linux, Linux Mint, BackTrack, elementary OS
Posts: 26

Rep: Reputation: Disabled
Writing a C shell, how should I execute the command?


I have written a basic C shell. It prints out a [user@hostname] ~ $ prompt by using getenv(), then reads the input and executes it using system(lineCopy). However, as my friend has pointed out, this is not actually a shell. This is just executing a command through /bin/sh.

So how should I execute a command? I have heard of forking the process ID then calling exec(), but how is that done?
 
Old 07-27-2015, 03:09 PM   #2
smallpond
Senior Member
 
Registered: Feb 2011
Location: Massachusetts, USA
Distribution: Fedora
Posts: 4,140

Rep: Reputation: 1263Reputation: 1263Reputation: 1263Reputation: 1263Reputation: 1263Reputation: 1263Reputation: 1263Reputation: 1263Reputation: 1263
Should be something like:

Code:
pid_t pid;
int err;

pid=fork();
if (pid == -1) 
    err=errno;
else if (pid == 0) {
    execv(arg0, rest_of_args);
    err = errno;  // should never get here
}
else
    waitpid(pid, &err, 0);
 
1 members found this post helpful.
Old 07-28-2015, 02:13 AM   #3
Kslkgh
LQ Newbie
 
Registered: Jul 2015
Location: The computer
Distribution: Debian, Kali Linux, Linux Mint, BackTrack, elementary OS
Posts: 26

Original Poster
Rep: Reputation: Disabled
OK then, I've added a way of splitting the line into tokens, but when I try to build it, it throws this error:

main.c:151:12: error: arg0 undeclared (first use in this function)

I know what the error means, but what should it be set too?

Last edited by Kslkgh; 07-28-2015 at 02:33 AM.
 
Old 07-28-2015, 03:35 AM   #4
millgates
Member
 
Registered: Feb 2009
Location: 192.168.x.x
Distribution: Slackware
Posts: 852

Rep: Reputation: 389Reputation: 389Reputation: 389Reputation: 389
Quote:
Originally Posted by Kslkgh View Post
OK then, I've added a way of splitting the line into tokens, but when I try to build it, it throws this error:

main.c:151:12: error: arg0 undeclared (first use in this function)

I know what the error means, but what should it be set too?
arg0 should be the program you want to execute.
 
1 members found this post helpful.
Old 07-28-2015, 05:44 AM   #5
Kslkgh
LQ Newbie
 
Registered: Jul 2015
Location: The computer
Distribution: Debian, Kali Linux, Linux Mint, BackTrack, elementary OS
Posts: 26

Original Poster
Rep: Reputation: Disabled
I get the error vsh: Bad address when I try to run a command. (I've set up perror("vsh"); on all my functions).

What's going on?
 
Old 07-28-2015, 05:45 AM   #6
millgates
Member
 
Registered: Feb 2009
Location: 192.168.x.x
Distribution: Slackware
Posts: 852

Rep: Reputation: 389Reputation: 389Reputation: 389Reputation: 389
Quote:
Originally Posted by Kslkgh View Post
I get the error vsh: Bad address when I try to run a command. (I've set up perror("vsh"); on all my functions).

What's going on?
Can you post your code (or, at least, the relevant part of it)?
 
Old 07-28-2015, 07:43 AM   #7
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,862
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
> What's going on?

You might have one or more bugs in your code.

> I've set up perror("vsh"); on all my functions

Try to be more informative, eg:

Code:
rc= execve ();
if (rc) {
    perror ("execve");
}

Last edited by NevemTeve; 07-28-2015 at 07:44 AM.
 
Old 07-28-2015, 09:02 AM   #8
Kslkgh
LQ Newbie
 
Registered: Jul 2015
Location: The computer
Distribution: Debian, Kali Linux, Linux Mint, BackTrack, elementary OS
Posts: 26

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by millgates View Post
Can you post your code (or, at least, the relevant part of it)?
Yes I can. My code is as follows:
Code:
/*
Credit to Stephen Brennan (http://stephen-brennan.com/2015/01/16/write-a-shell-in-c/) for the split_line function.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

#define LINESIZE 1024
#define RL_BUFSIZE 1024
#define TOK_BUFSIZE 64
#define TOK_DELIM " \t\r\n\a"

char **split_line(char *line)
{
    int bufsize = TOK_BUFSIZE, position = 0;
    char **tokens = malloc(bufsize * sizeof(char*));
    char *token, **tokens_backup;

    if (!tokens) {
        fprintf(stderr, "vsh: allocation error\n");
        exit(EXIT_FAILURE);
    }
		
    token = strtok(line, TOK_DELIM);
    while (token != NULL) {
        tokens[position] = token;
        position++;
	
        if (position >= bufsize) {
            bufsize += TOK_BUFSIZE;
            tokens_backup = tokens;
            tokens = realloc(tokens, bufsize * sizeof(char*));
            if (!tokens) {
		        free(tokens_backup);
                fprintf(stderr, "vsh: allocation error\n");
                exit(EXIT_FAILURE);
            }
        }
	
        token = strtok(NULL, TOK_DELIM);
     }
    tokens[position] = NULL;
    return tokens;
}
/*
int execute(char *command)
{
	system(command);
	
	return 1;
}
*/
int main(int argc, char **argv)
{
	char *cmd;
	int finish = 0;
	char *user = getenv("USER");
	char hostname[1024];
	char *pwd = getenv("PWD");
    char line[LINESIZE] = {0};
    char lineCopy[LINESIZE] = {0};
    char *command = NULL;
    char **args;
    
    // Print out a welcome message
    printf("Welcome to vsh\n");
	
	// Print out the message of the day
    system("/bin/cat /etc/motd");
    
    printf("\n");
	
	gethostname(hostname, 1024);
	
    while (!finish)
    {
		// Update 'pwd' variable
		pwd = getenv("PWD");
		// Print out prompt
		printf("[");
        printf(user);
        printf("@");
        printf(hostname);
        printf("] ");
        printf(pwd);
        printf(" $ ");
        fflush(stdout);
        
        if(NULL == fgets(line, sizeof line, stdin))
        {
			// If NULL, leave vsh
            finish = 1;
            printf("\nLeaving vsh\n");
        }
        else
        {
			// If there is something there...
            printf("The command read was s", line);
            printf("\n");
            char *newLine = strchr(line, '\n');
            
            if(newLine != NULL)
            {
                *newLine = '\0';
                strcpy(lineCopy, line);
            }
            
            command = strchr(line, ' ');
            
            if(command != NULL)
            {
                *command++ = '\0';
                printf("Command= _%s_\n\n", line);                
            }
            // Is the command pre-programmed into vsh?
            if(strcmp(line, "cd") == 0)
            {
				printf("Sorry, CD does not work in this version of vsh Shell. :(\n");
			}
            else if(strcmp(line,"programmer") == 0)
            {
                printf("The programmer of vsh is George Gibson\n");
            }
            else if(strcmp(line,"ver") == 0)
            {
                printf("The current version is 1.0\n");
            }
            else if(strcmp(line, "help") == 0)
            {
				printf("Help for vsh\n\nType a command to run it. The builtin commands are : cd, programmer, ver, help and exit.Their functions are as follows:\n\ncd: Change Directory. Changes the current working directory.\n\nprogrammer: Display the programmers of vsh.\n\nver: Display the version of vsh you are running.\n\nhelp: Launches this program.\n\nexit: Leave vsh Shell.\n\nThese are the only builtins in vsh, but note that if you define a command in /bin with one of these names, your program will not run in vsh unless you use it's explicit name (/bin/yourprogram) rather than \"yourprogram\". vsh 1.0 written and programmed by George Gibson.");
			}
            else if(strcmp(line,"exit") == 0)
			{
				finish = 1;
				printf("\nLeaving vsh\n");
			}
			else if(strcmp(line, "scroll") == 0)
            {
				// Easter egg
				printf(" _\n(@)\\__________  _\n|-|           \\(@)\n| |    VSH!    |-|\n|_|            | |\n(@)\\__________ |_|\n ~            \\(@)\n                ~\n\nWell done for finding the hidden easter egg in vsh!\n\n");
			}  
			// If command is not a vsh built-in, run the command
            else
            {
                args = split_line(line);
                pid_t pid;
				int err;

				pid=fork();
				if(pid == -1) 
					perror("vsh");
				else if(pid == 0) {
					execv(args[0], args);
					perror("vsh");  // Should never get here
				}
				else {
					waitpid(pid, &err, 0);
				}
            }
        }
    }
    
    return 0;
}
Sorry about the indentation. Don't know why the clipboard did this (in my editor it was all perfect four-space indents).

Last edited by Kslkgh; 07-28-2015 at 09:50 AM. Reason: Updated Code
 
Old 07-28-2015, 09:19 AM   #9
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,862
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
What's the point of calling 'read_line' after 'fgets'? The total input can be found in variable 'line'(okay, you have overwritten the first space with '\0', but that can be undone)
 
1 members found this post helpful.
Old 07-28-2015, 09:42 AM   #10
Kslkgh
LQ Newbie
 
Registered: Jul 2015
Location: The computer
Distribution: Debian, Kali Linux, Linux Mint, BackTrack, elementary OS
Posts: 26

Original Poster
Rep: Reputation: Disabled
Sorry, that was a mistake. Removed that and now it will work, but only if I use an explicit path (/bin/some_command), not how you would usually execute it (some_command). If you try and just do some_command, it will give the error vsh: No such file or directory. Should I just append /bin/ to the start of the command, or is there a better solution?
 
Old 07-28-2015, 02:03 PM   #11
rhubarbdog
Member
 
Registered: Apr 2015
Location: Yorkshire, England
Distribution: Linux Mint
Posts: 145

Rep: Reputation: Disabled
Arg0 can be any string, this is the name recieved by the receiving process, usually basename(program_path).

You need to change execv line to read

ret=execv(...

And add an if(ret) before the perror after execv

Only call perror if the previous call has returned one. Read the man page about execv to learn more.
 
Old 07-29-2015, 01:33 AM   #12
genss
Member
 
Registered: Nov 2013
Posts: 741

Rep: Reputation: Disabled
Quote:
Originally Posted by Kslkgh View Post
Sorry, that was a mistake. Removed that and now it will work, but only if I use an explicit path (/bin/some_command), not how you would usually execute it (some_command). If you try and just do some_command, it will give the error vsh: No such file or directory. Should I just append /bin/ to the start of the command, or is there a better solution?
you get PATH from the environment
then you go through paths from it and try them in order
 
1 members found this post helpful.
Old 07-29-2015, 01:41 AM   #13
Kslkgh
LQ Newbie
 
Registered: Jul 2015
Location: The computer
Distribution: Debian, Kali Linux, Linux Mint, BackTrack, elementary OS
Posts: 26

Original Poster
Rep: Reputation: Disabled
I've managed to work around this by using exevp(args[0], args) in order to execute rather than execv(args[0], args). Thanks for all of your help guys, the code can be found at GitHub if you want it. Any optimisations or improvements let me know on this thread.
 
  


Reply

Tags
shell



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] automatically execute command in a new shell sharky Programming 4 01-24-2012 03:03 PM
Execute shell command in a variable hankta Linux - Newbie 4 08-25-2011 01:06 AM
Can I execute a shell command and put the result in command field? fran4tw Linux - General 9 08-08-2011 08:27 PM
[SOLVED] Effect of using eval to execute a command as opposed to writing it on the commandline daudiam Programming 1 06-20-2010 01:11 AM
PHP can't execute shell command ! rome Programming 1 11-15-2005 09:12 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 06:37 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration