I've been writing a shell program in C. The program is working as expected in Linux (Ubuntu 16.04) but I'm getting unexpected output in MacOS (10.14.2 Mojave).
/* A shell program.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
void input(char* argv[]);
void print_arr(char *argv[]); // For debugging
int
main(void)
{
while (1)
{
pid_t pid;
char *argv[100];
// Display shell prompt
write(1, "(ash) $ ", 8);
// Take user input
input(argv);
// print_arr(argv); // DEBUG STATEMENT
if (argv[0] != NULL)
{
// Exit if exit command is entered
if (strcmp(argv[0], "exit") == 0)
{
exit(0);
}
// Create child process
if ((pid = fork()) > 0)
{
wait(NULL);
}
else if (pid == 0)
{
// print_arr(argv); // DEBUG STATEMENT
execvp(argv[0], argv);
printf("%s: Command not found\n", argv[0]);
exit(0);
}
else
{
printf("Fork Error!\n");
}
}
}
}
/* Takes input from user and splits it in
tokens into argv. The last element in
argv will always be NULL. */
void
input(char* argv[])
{
const int BUF_SIZE = 1024;
char buf[BUF_SIZE];
int i;
buf[0] = '\0';
fgets((void*) buf, BUF_SIZE, stdin);
i = 0;
argv[i] = strtok(buf, " \n\0");
while (argv[i] != NULL)
{
argv[++i] = strtok(NULL, " \n\0");
}
}
/* Print argv for debugging */
void
print_arr(char *argv[])
{
int i = 0;
while (argv[i] != NULL)
{
printf("%d: %s\n", i, argv[i]);
++i;
}
}
In Linux:
(ash) $ ls
// files and folders are listed
In MacOS (with debug statements):
(ash) $ ls
0: p?M??
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
I don't understand that why are the contents of char* argv[] getting modified across fork()?
I've also tried it in the default clang compiler and brew's gcc-4.9, the results are same.
When a program behaves different for no good reason, that's a VERY good sign of undefined behavior. And it is also the reason here.
The array buf is local to the function input and ceases to exist when the function exits.
One way of solving this is to declare buf in main and pass it to input. You will also need the size of the buffer for fgets.
void
input(char * argv[], char * buf, size_t size)
{
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
Another solution (although I suspect many will frown upon it) is to declare buf as static, but then you would need to change BUF_SIZE to a #define or a hard coded value, since you cannot have a static VLA.
#define BUF_SIZE 1024
void
input(char * argv[])
{
static char buf[BUF_SIZE];
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
I removed the cast to void* since it's completely unnecessary. I also changed the while loop to a for loop to make the loop variable local to the loop.
Related
I am trying to use the execv() function.
I am trying to pass in my argument command to the left side.
execv(file,arguments);
I am using a char * to parse the incoming user input for my shell.
The second argument of execv takes a char * const*.
Is there a way I can cast a char * const to a char * const*?
I try this below,
char * arg;
char *const a[] = (char *const)arg;
error: invalid initializer
char *const a[] = (char *const)arg;
^
But it does not work and gives me errors.
Help would be apprecieated.
The error in char *const a[] = (char *const)arg; is not due to an improper conversion. It is because char *const a[] declares an array, and the initializers for an array must be in braces1, { … }, but you have specified just one initializer without braces.
Furthermore, the argv parameter to execv should be an array of pointers in which the first points to a string containing the file name of the program being executed (this is by convention, not required) and the last is a null pointer. Thus, your definition of a ought to be something like:
char * const a[] = { FileNameOfProgram, arg, NULL };
Footnote
1 Except when a string literal is used to initialize an array, but that is not the case here.
You're trying to initialize an array. Instead of doing this,
char * arg;
char *const a[] = (char *const)arg;
do this:
char * arg;
char *const a[] = {(char *const)arg};
It's quite normal to do an execv after eliminating the command name and some of the first parameters. For example, if you have some code like (you had better to post a complete and verifiable example) let's assume you are doing something like this (if you want an example, look for the xargs(1) manpage, you have a command, and after processing the options and their parameters, you want to eliminate all of them, and execute the rest as if it was a command line, e.g. I have a command to execute repeatedly a command, delaying some specified time, like:
cont -t 0.1 -- df -k .
I use <getopts.h> to process the options of my cont program, then execute repeatedly the command df -k. Options allow to show a version for the program, to specify timeout, be verbose, or the number of times to execute the command. I wrote it just now, to show you how to do it (the example includes fork(2) use, execvp(2) and redirection to capture the output of the command to be able to go back to the origin, once known the number of lines we have received, the program uses an ANSI escape to move the cursor back to the beginning.)
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define F(_fmt) "%s:%d: " _fmt, __FILE__, __LINE__
#define FLAG_VERBOSE (1<<0)
#define FLAG_VERSION (1<<1)
#define FLAG_DELAY (1<<2)
#define FLAG_NTIMES (1<<3)
int flags = 0;
useconds_t delay = 1000000;
size_t ntimes;
void doVersion(void)
{
fprintf(stderr,
"cont: v1.0\n"
"(C) Luis Colorado. All rights reserved.\n"
"License: BSD\n");
exit(EXIT_SUCCESS);
}
ssize_t loop(int argc_unused, char **argv)
{
int fd[2];
int res = pipe(fd);
res = fork();
if (res < 0) {
fprintf(stderr,
F("fork: ERROR %d: %s\n"),
errno,
strerror(errno));
return -1;
} else if (res == 0) { /* child */
close(fd[0]); /* not going to use it */
dup2(fd[1], 1); /* redirect output to pipe */
close(fd[1]);
execvp(argv[0], argv);
fprintf(stderr,
F("execv: ERROR %d: %s\n"),
errno, strerror(errno));
return -1;
} else { /* parent */
pid_t cld_pid = res;
close(fd[1]); /* no writing to the pipe */
FILE *f = fdopen(fd[0], "rt"); /* just reading */
int c;
size_t lines = 0;
while((c = fgetc(f)) != EOF) {
if (c == '\n') lines++;
putc(c, stdout);
}
wait(NULL);
return lines;
}
} /* loop */
int main(int argc, char **argv)
{
int opt;
float t;
while ((opt = getopt(argc, argv, "t:Vvn:")) >= 0) {
switch(opt) {
case 't': flags |= FLAG_DELAY;
t = atof(optarg);
break;
case 'V': flags |= FLAG_VERSION;
break;
case 'v': flags |= FLAG_VERBOSE;
break;
case 'n': flags |= FLAG_NTIMES;
ntimes = atoi(optarg);
break;
/* ... */
}
}
if (flags & FLAG_VERSION)
doVersion();
/* the next pair of sentences is like `shift optind' in the shell. */
/* trick, don't move the parameters, just move the pointer */
argc -= optind; /* adjust the number of parameters. */
argv += optind; /* advance the pointer to the proper place */
/* NOW, argc && argv are identical to the original ones, but lacking the
* first `optind' argument strings. As the original string array ended
* in a NULL, there's no need to construct it from allocating memory.
* Anyway, we're not going to use after it's consumed in main(). */
if (flags & FLAG_VERBOSE) {
char *sep = "About to execute: ";
int i;
for (i = 0; i < argc; i++) {
fprintf(stderr, "%s%s", sep, argv[i]);
sep = " ";
}
fprintf(stderr, "\n");
}
if (flags & FLAG_DELAY) {
delay = t * 1.0E6;
}
size_t total_lines = 0;
ssize_t n = 0;
while(!(flags & FLAG_NTIMES) || ntimes--) {
/* move up as many lines as input from subcommand */
if (n) printf("\r\033[%ldA#\b", n);
n = loop(argc, argv);
if (n < 0) {
/* we have already written the error */
exit(EXIT_FAILURE);
}
usleep(delay);
total_lines += n;
}
if (flags & FLAG_VERBOSE) {
fprintf(stderr,
F("Total lines: %lu\n"),
total_lines);
}
exit(EXIT_SUCCESS);
}
you can download a complete version of this program from Github
I'm trying to write a shell program in c.
The program needs to have multiple processes created by fork function and be able to print multiple output line in one command.
For example, like linux terminal, if input is "ls ; ps ; pwd ;", the output should be like this.
$./shell
shell> ls ; ps ; pwd ;
(ls output)
(ps output)
(pwd output)
And it should be able to open a file and display the command list and the output that file contains.(batch mode I guess?)
Let's say these command lists are in the batch file.
batch
1 ls
2 ps
3 ls ; pwd ; ps
And the output is
$./shell batch
shell> ls
shell> (ls output)
shell> ps
shell> (ps output)
shell> ls ; pwd ; ps
shell> (ls output)
(ps output)
(pwd output)
Here's the code I wrote
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
void command(char* myargs[][10], char* buffer);
int tokenizing(char* myargs[][10], char* buffer);
int main(int argc, char* argv[]) {
int fd;
char buffer[200];
char* myargs[10][10];
char* token;
if(argc >= 2) {
if((fd=open(argv[1], O_RDONLY)) == -1)
printf("cannot open file\n");
else {
read(fd, buffer, 200);
printf("%s\n", buffer);
token = strtok(buffer, "\n");
while(token != NULL) {
printf("%s\n", token);
command(myargs, token);
token = strtok(NULL, "\n");
}
return 0;
}
}
while(1) {
printf("prompt> ");
if(fgets(buffer, 200, stdin) == NULL||
strcmp(buffer, "quit\n") == 0)
break;
command(myargs, buffer);
}
return 0;
}
void command(char* myargs[][10], char* buffer) {
int rc = fork();
if(rc < 0) {
fprintf(stderr, "fork failed\n");
} else if(rc == 0) {
int n = tokenizing(myargs, buffer);
for(int i = 0 ; i < n; i++) {
int rc2 = fork();
if(rc2 < 0) {
fprintf(stderr, "for failed\n");
} else if(rc2 == 0) {
execvp(myargs[i][0], myargs[i]);
printf("%s: command not found\n", myargs[i][0]);
exit(0);
} else {
wait(NULL);
}
}
exit(0);
}
else {
wait(NULL);
}
}
int tokenizing(char* myargs[][10], char* buffer) {
int i = 0;
int j = 0;
int k = 0;
char* token;
char* subCommand[10];
token = strtok(buffer, ";\n");
while(token != NULL) {
subCommand[k] = token;
k++;
token = strtok(NULL, ";\n");
}
for(int i = 0; i < k; i++) {
token = strtok(subCommand[i], " \n");
while(token != NULL) {
myargs[i][j] = token;
j++;
token = strtok(NULL, " \n");
}
myargs[i][j] = NULL;
j=0;
}
}
This code works fine but has some issues. When this code runs with a batch file, I struggle with some errors.
When the program is executed, I think the output should be like the above image file - as far as I know.
But frequently the program comes out with some weird command line that I didn't even type. These results just happen alternatively.
In addition, if you see 'ps' lists, you can see two shell programs are running.
Can you guys please help me solve these problems?
'strings' in C need a NUL terminator. Calling strng functions like printf("%s....") and strtok() on char arrays that are not securely NUL-terminated results in undefined behaviour.
read() returns a value. You can use it to load a terminator into 'buffer'. To be super-safe, you should attempt to read only [buffer size -1] chars to ensure that there will always be enough space for the terminator, eg:
buffer[read(fd, buffer, 199)]='\0';
I am working on pipeline system with string in C. For example I write this line to console ./pipeline LOWERCASE REVWORD SQUASHWS < stringFile.txt. It should be any like this P0 -> P1 -> P2 -> ...-> Pn. P0 load string from file stringFile.txt and send it to P1.. I can work with pipe(send and read), but I dont know how to work with N processes. It should be any like this. Can you give me any advice or example? Thank you
while(is_something_to_read) {
load_from_previous_process();
do_process(); // for example LOWERCASE
send_to_next_process();
}
Patrik,
I wrote a program that simulate a shell that will spawn a given amount of children and each process will then communicate with another process. Since the number of processes can vary I decided to use a 2D array for the pipes. In the below code NUM_PROCS refers to the amount of processes that will be running (including the parent).
I declare it
int pipes[NUM_PROCS][2];
After this, I create the pipes
for(i = 0; i < NUM_PROCS; i++)
{
if((pipe(pipes[i])) < 0)
{
perror("Failed to open pipe");
}
}
This is a shell program that i wrote for practise.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#define MAXARGS 256
char ** getargs(char * cmd) {
char** argsarray;
int nargs = 0;
int nlen = strlen(cmd);
int i = 0;
argsarray = (char**) malloc(sizeof(char*) * MAXARGS);
argsarray[0] = strtok(cmd," ");
i = 0;
while (argsarray[i] != NULL){
i++;
argsarray[i] = strtok(NULL," ");
}
return argsarray;
}
int main(void){
pid_t childpid;
int fd[256][2];
char cmd[256];
char * sepCmd[256];
char * pch;
printf("Please enter a command sequence: \n");
gets(cmd);
printf("You have entered: %s ....%d\n", cmd,strlen(cmd));
printf("Attempting to split up command: \n");
pch = strtok (cmd, "|");
int count = 0;
while (pch != NULL && count < 256) {
printf("%s\n", pch);
sepCmd[count] = pch;
printf("The value in this array value is: %s\n", sepCmd[count]);
pch = strtok (NULL, "|");
count++;
}
char ** argue;
int k;
/* Block that deals with the first command given by the user */
k = 0;
pipe(fd[k]);
if(!fork()) {
dup2(fd[k][1], STDOUT_FILENO);
close(fd[k][0]);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
/*Loop that will control all other comands except the last*/
for(k = 1; k <= count - 2; k++) {
close(fd[k-1][1]);
pipe(fd[k]);
if(!fork()) {
close(fd[k][0]);
dup2(fd[k-1][0], STDIN_FILENO);
dup2(fd[k][1], STDOUT_FILENO);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
}
/*Block that will take care of the last command in the sequence*/
k = count - 1;
close(fd[k-1][1]);
if(!fork()) {
dup2(fd[k-1][0], STDIN_FILENO);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
while(waitpid(-1, NULL, 0) != -1);
}
I have an assignment for school where I have to create a shell which can do the following:
read incoming command, parse each part of command
fork child process and execute each command without (< > >> |)
successfully execute each command with <, >, >>
successfully execute each command with |
I am seriously lost... I am new to shell and I have no clue on what to do from here.
My code gives me an error stating segmentation fault (core dumped). Any and all help will be greatly appreciated.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define MAX_ARG 10
int main()
{
char line [256];
char prompt[] = "sh2 % ";
char command[256], *args[MAX_ARG];
int pid;
char status;
int i;
/* spit out the prompt */
printf("%s", prompt );
while( fgets(line, sizeof line, stdin) != NULL)
{
/* fgets leaves '\n' in input buffer. ditch it */
line [strlen(line)-1] = '\0';
while (line != NULL)
{
//parse command and arg
args[0] = strtok(line, " "); //grab command
for (i=1; i<MAX_ARG; i++) //grab arguments, to assume max = 10?
{
//if a single command with arguments then set command & argument
//for (i>0)
{
// check to see if the command is 'exit'
if(!strcmp("exit", args[i]))
{
exit(0);
}
{
int p[2];
pipe(p)
if (fork() == 0) //child
{
close (0);
dup(p[0]);
exec("cmd2");
}
else
{
close(1);
close(p[0]);
close(p[1]);
dup(p[1]);
exec("cmd1");
}
close(0);
open("stdout.txt", "r");
if (fork()== 0)
{
exec("cmd3");
}
}
else if (!strcmp(">", args[i]))
open("stderr.txt". "w")
if (fork() == 0)
{
exec("cmd1");
}
}
else if (!strcmp(">>", args[i]))
{
close(1);
open("stdout_stderr.txt", "w");
if (fork() == 0)
{
close(2);
dup(1);
exec("cmd2");
}
}
else
{
pid = fork();
if (pid == 0)
{
status = execvp(command,args);
exit(0);
}
else
{
waitpid(-1);
}
}
}
}
}
}
return 0;
}
You have quite a few things wrong in your program, so it's hard to point to one line and say change this. I think you are trying to do too much at once and not building on a solid base.
In programming you want to start small and build on your progress, your professor did you a favor by lining up the steps:
read incoming command, parse each part of command
fork child process
and execute each command without (< > >> |)
successfully execute
each command with <, >, >>
successfully execute each command with |
Try to get #1 working befor moving on to #2 and as mentioned before using functions is going to help a lot. I would suggest looking at this post http://bytes.com/topic/c/answers/215994-writing-shell-c which will give you a simple shell you could model from. Here is a baseline parser to get you started on #1 (based on the post mentioned)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char line[4096] = {0};
char safeline[4096] = {0};
int done = 0;
int i = 0;
char *s = line;
char **t = NULL;
char *prompt = ">";
char *args = NULL;
char *nl = NULL;
int main(void)
{
while(!done)
{
printf("%s", prompt);
fflush(stdout);
if(NULL == fgets(line, sizeof line, stdin))
{
t = NULL;
done = 1;
}
else
{
nl = strchr(line, '\n');
if(nl != NULL)
{
*nl = '\0';
strcpy(safeline, line);
}
else
{
int ch;
printf("Line too long! Ignored.\n");
while((ch = getchar()) != '\n' && ch != EOF)
{
continue;
}
if(ch == EOF)
{
done = 1;
}
}
args = strchr(line, ' ');
if(args != NULL)
{
*args++ = '\0';
}
if(!done)
{
printf("command - %s : args - %s\n",s, args);
}
}
}
}
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..