Here is my code, for my own shell in C. When compiling I get an error: use of undeclared identifier 'output'. Here are examples of some of the errors when compiling:
error: use of undeclared identifier 'output' char
input[100];output[100];
test3.c:53:15: error: use of undeclared identifier 'output'
strcpy(output,args[i+1]);
^
test3.c:53:15: error: use of undeclared identifier 'output'
test3.c:60:8: warning: implicit declaration of function 'open' is
invalid in C99
[-Wimplicit-function-declaration] j = open(input, O_RDONLY, 0);
^
test3.c:60:20: error: use of undeclared identifier 'O_RDONLY' j =
open(input, O_RDONLY, 0);
^
test3.c:61:29: error: use of undeclared identifier 'O_RDONLY'
if ((j = open(input, O_RDONLY, 0)) < 0) {
test3.c:70:12: warning: implicit declaration of function 'creat' is
invalid in
C99 [-Wimplicit-function-declaration] if ((i= creat(output , 0644)) < 0) {
test3.c:70:18: error: use of undeclared identifier 'output' if ((i=
creat(output , 0644)) < 0) {
Here is my code:
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "signal.h"
#include "unistd.h"
void prompt(char*);
void execute( char* );
char** parse( char* );
int main( int ac, char* av[] )
{
char input[255]; // buffer for supporting command
signal( SIGINT, SIG_IGN ); // ignore ctrl-c
while(1)
{
prompt(input);
execute( input );
}
};
void execute( char* str)
{
int fork_result, status, i = 0,j=0,in=0,out=0;
char input[100];output[100];
char** args = parse( str ); // splits the user command into arguments
fork_result = fork(); // attempt to fork
if ( fork_result == -1 ) // failure
{
perror("Failed to fork\n");
exit(1);
}
else if ( fork_result == 0 ) // I'm the child
{
for(i=0;args[i]!='\0';i++)
{
if(strcmp(args[i],"<")==0)
{
args[i]=NULL;
strcpy(input,args[i+1]);
in=2;
}
if(strcmp(args[i],">")==0)
{
args[i]=NULL;
strcpy(output,args[i+1]);
out=2;
}
}
if (in)
{
j = open(input, O_RDONLY, 0);
if ((j = open(input, O_RDONLY, 0)) < 0)
{
perror("Couldn't open input file");
exit(0);
}
dup2(j, 0);
close(j);
}
if (out)
{
if ((i= creat(output , 0644)) < 0)
{
perror("Couldn't open the output file");
exit(0);
}
dup2(i, STDOUT_FILENO);
close(i);
}
execvp( args[0], args );
perror("failed to exec\n");
exit(2);
}
else // I'm the parent
{
// wait here
wait(&status); // wait for child to finish
free( args ); // free dynamic memory
}
}
char** parse( char* str )
{
char** args = malloc( 256 );
int i = 0;
args[i] = strtok( str, " " );
while( args[i] )
{
i++;
args[i] = strtok( NULL, " " );
}
return args;
}
void prompt(char* input)
{
printf("$ "); // print prompt
fgets( input, 255, stdin );
input[strlen(input)-1] = '\0'; // overwrite \n with \0
if ( strcmp( input, "exit" ) == 0 ) // shell command
exit(0);
}
char input[100];output[100];
You want:
char input[100], output[100];
Also add: #include <fcntl.h>
In general, man open (and other functions you use) is your friend -- it tells you what #includes to add.
There are many more potential bugs and arbitrary limitations in your code. Some examples:
void execute( char* str)
{
char input[100], output[100];
...
if(strcmp(args[i],"<")==0)
{
args[i]=NULL;
strcpy(input,args[i+1]); // possible stack buffer overflow.
if(strcmp(args[i],">")==0)
{
args[i]=NULL;
strcpy(output,args[i+1]); // possible stack buffer overflow
char** parse( char* str )
{
char** args = malloc( 256 ); // limit of 256/sizeof(char*) parameters.
// on a 64-bit system, if more than 32 parameters are supplied ...
args[i] = strtok( NULL, " " ); // ... possible heap buffer overflow.
fgets( input, 255, stdin ); // arbitrary limit of 254 characters on command line.
There is no guarantee that the string ends with \n:
input[strlen(input)-1] = '\0'; // overwrite \n with \0
If I was grading this "shell", I'd give it an "F".
There are multiple errors in your code.
1. On line 27 you need to separate the two variable definitions of input and output with a comma instead of a semicolon char input[100], output[100]; or specify the type of output like char input[100]; char output[100]; You have done this already in the line above.
2. The compiler complains about missing definitions of the function open and the identifier O_RDONLY. This can be fixed by adding #include "fcntl.h" to your includes at the top of the file.
After these changes, the code compiles fine for me (with gcc 5.4.0):
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "signal.h"
#include "unistd.h"
#include "fcntl.h"
void prompt(char*);
void execute( char* );
char** parse( char* );
int main( int ac, char* av[] )
{
char input[255]; // buffer for supporting command
signal( SIGINT, SIG_IGN ); // ignore ctrl-c
while(1)
{
prompt(input);
execute( input );
}
};
void execute( char* str)
{
int fork_result, status, i = 0,j=0,in=0,out=0;
char input[100], output[100];
char** args = parse( str ); // splits the user command into arguments
fork_result = fork(); // attempt to fork
if ( fork_result == -1 ) // failure
{
perror("Failed to fork\n");
exit(1);
}
else if ( fork_result == 0 ) // I'm the child
{
for(i=0;args[i]!='\0';i++)
{
if(strcmp(args[i],"<")==0)
{
args[i]=NULL;
strcpy(input,args[i+1]);
in=2;
}
if(strcmp(args[i],">")==0)
{
args[i]=NULL;
strcpy(output,args[i+1]);
out=2;
}
}
if (in)
{
j = open(input, O_RDONLY, 0);
if ((j = open(input, O_RDONLY, 0)) < 0)
{
perror("Couldn't open input file");
exit(0);
}
dup2(j, 0);
close(j);
}
if (out)
{
if ((i= creat(output , 0644)) < 0)
{
perror("Couldn't open the output file");
exit(0);
}
dup2(i, STDOUT_FILENO);
close(i);
}
execvp( args[0], args );
perror("failed to exec\n");
exit(2);
}
else // I'm the parent
{
// wait here
wait(&status); // wait for child to finish
free( args ); // free dynamic memory
}
}
char** parse( char* str )
{
char** args = malloc( 256 );
int i = 0;
args[i] = strtok( str, " " );
while( args[i] )
{
i++;
args[i] = strtok( NULL, " " );
}
return args;
}
void prompt(char* input)
{
printf("$ "); // print prompt
fgets( input, 255, stdin );
input[strlen(input)-1] = '\0'; // overwrite \n with \0
if ( strcmp( input, "exit" ) == 0 ) // shell command
exit(0);
}
Related
I am writing code for simple shell in C, and for some unknown reason I am getting error when trying to execute man command, while commands like ls, cat or other works fine. I want to have function for searching path of command.
This is error:
man: can't execute cat: No such file or directory
man: command exited with status 255: (cd /usr/share/man && /usr/lib/man-db/zsoelim) | (cd /usr/share/man && /usr/lib/man-db/manconv -f UTF-8:ISO-8859-1 -t ISO-8859-1//IGNORE) | (cd /usr/share/man && tbl) | (cd /usr/share/man && nroff -mandoc -rLL=88n -rLT=88n -Tutf8)
#include<string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdio.h>
char *searchPath(char *cmd) {
int poljelen = 2;
char **stringovi = malloc(poljelen * sizeof(char*));
char *path = getenv("PATH");
int i = 0;
while((stringovi[i] = strsep(&path, ":")) != NULL) {
i++;
if (i >= poljelen) {
poljelen = poljelen + i;
stringovi = realloc(stringovi, poljelen * sizeof(char*));
if (stringovi == NULL) {
fprintf(stderr, "%s", "Alloc error\n");
exit(0);
}
}
}
stringovi[i] = '\0';
i = 0;
while(*stringovi[i] != '\0') {
int pathlen = strlen(stringovi[i]);
int cmdlen = strlen(cmd);
char path[pathlen + cmdlen + 2];
strcpy(path, stringovi[i]);
strcat(path, "/");
strcat(path, cmd);
if (access(path, F_OK) == 0) {
char *pathname = malloc(sizeof(char) * (pathlen + cmdlen + 2));
if (!pathname) {
return NULL;
}
strcpy(pathname, path);
pathname[strlen(pathname)] = '\0';
return pathname;
}
else {
i++;
continue;
}
}
return NULL;
}
int main() {
char *tokens[3];
tokens[0] = "man"; //if you put here "ls"
tokens[1] = "ls"; // and here "-al" its working
tokens[2] = NULL;
char *pathh = searchPath(tokens[0]);
if (pathh == NULL) {
fprintf(stderr, "Error: command not found\n");
return 1;
}
char stringg[strlen(pathh) + 1];
strcpy(stringg, pathh);
pid_t childP;
int status = 0;
childP = fork();
if (childP == 0) {
if (execv(stringg, tokens) == -1) {
fprintf(stderr, "Error: execv failed: %s\n", strerror(errno));
exit(1);
}
}
else if (childP == -1) {
fprintf(stderr, "Error: fork failed: %s\n", strerror(errno));
return 1;
}
else {
waitpid(childP, &status, WUNTRACED);
}
}
Your use of strsep() corrupts your PATH environment variable.
Per the Linux strsep() man page:
... this function finds the first token in
the string *stringp, that is delimited by one of the bytes in the
string delim. This token is terminated by overwriting the
delimiter with a null byte ('\0') ...
By calling strsep() with the value returned from getenv("PATH") you corrupt your PATH environment variable, leaving it as only the first component of the original value.
You should make a copy of the value returned from getenv("PATH") and split that into tokens.
Here are two problems in the program
First, is that when I uncomment the pthread_join() in the main function, there will be a seg fault, other wise the program will run...
Second, is that the output file will be missing the first letter of each word that has stored in the global variable words from last read file. So, for example, there are two files:
one has words "abc abc abc abc abc abc abc abc".
the second has words "def def"
if i input 5 for the second argument when calling a.out, the output in the output file will be
abc
abc
abc
abc
abc
bc
bc
bc
def
def
This is also a werid thing I could not figure out why.
/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <ctype.h>
#include <pthread.h>
#include "hw3.h"
int index_;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
typedef struct files
{
char *inputfile;
FILE * outputfile;
} files;
void * readFile( void *arg ){
files *info = (files *)arg;
char fileName[80];
strncat(fileName, (info->inputfile), 79);
fileName[80] = '\0';
FILE *outputfd = info->outputfile;
FILE* fd;
fd = fopen(fileName, "r");
if ( fd == NULL) {
fprintf(stderr, "ERROR:<open() failed>\n");
}
printf("TID %d: Opened \"%s\"\n", (unsigned int)pthread_self(), fileName);
fflush(stdout);
int rc;
char ch[1] = {0};
char word[80] = {0};
ch[0] = fgetc(fd);
pthread_mutex_lock(&mutex);
while( ch[0] != EOF){
if( isalnum(ch[0]) ){
// char str = ch[0];
strncat(word, ch, 1);
}
else{//it's a word
if( strlen( word ) >= 2 ){
words[index_] = word;
printf("TID %d: Stored \"%s\" in shared buffer at index [%d]\n",(unsigned int)pthread_self(), word, index_ );
if( index_+ 1 == maxwords ){
index_ = 0;
printf("MAIN: Buffer is full; writing %d words to output file\n", maxwords);
for( unsigned int i = 0; i<maxwords; i++ ){
rc = fwrite( words[i], 1, sizeof(words[i]), outputfd );
fwrite( "\n", 1, sizeof("\n"), outputfd );
if( rc == -1 ){
fprintf(stderr, "ERRPR:<write() failed>\n");
//return EXIT_FAILURE;
}
}
}
else{
index_ ++;
}
}
for(int i = 0; i< strlen(word); i++){
word[i] = '\0';
}
}
ch[0] = fgetc(fd);
}
pthread_mutex_unlock(&mutex);
printf("TID %d: Closed \"%s\"; and exiting\n", (unsigned int)pthread_self(), fileName );
fclose(fd);
pthread_exit( NULL );
}
int main( int argc, char * argv[] ){
if(argc != 4){
fprintf(stderr, "ERROR: Invalid arguments\nUSAGE: ./a.out <input-directory> <buffer-size> <output-file>\n");
return EXIT_FAILURE;
}
//dynamically allocated words buffer with argument 2
maxwords = atoi(argv[2]);
words = (char**)calloc(maxwords, sizeof(char*) );
if ( words == NULL)
{
fprintf( stderr, "ERROR:<word calloc() failed\n>" );
return EXIT_FAILURE;
}
printf("MAIN: Dynamically allocated memory to store %d words\n", maxwords);
fflush(stdout);
//open/create output file of the third argument
FILE* outputfd = fopen (argv[3], "w");
if ( outputfd == NULL )
{
perror( "open() failed" );
return EXIT_FAILURE;
}
DIR * dir = opendir( argv[1] );
if(dir == NULL){
perror("ERRPR:<opendir() failed>");
return EXIT_FAILURE;
}
chdir(argv[1]);
printf("MAIN: Opened \"%s\" directory\n", argv[1]);
fflush(stdout);
pthread_t tid[10];
index_ = 0;
int i = 0;//files index
struct dirent * file;
//files allfiles[20];
char fileName[80];
int rc;
//-----------------------------------------------------------------------
// while loop reads all files in the directory
while ( ( file = readdir( dir ) ) != NULL )
{
struct stat buf;
rc = lstat( file->d_name, &buf ); /* e.g., "xyz.txt" */
/* ==> "assignments/xyz.txt" */
if ( rc == -1 ){
fprintf(stderr, "ERRPR:<lstat() failed>\n");
return EXIT_FAILURE;
}
if ( S_ISREG( buf.st_mode ) )
{
// printf( " -- regular file\n" );
// fflush(stdout);
strncpy(fileName, file->d_name, 79);
files info;
info.inputfile = fileName;
info.outputfile = outputfd;
//printf("%d",i);
printf("MAIN: Created child thread for \"%s\"\n",fileName);
rc = pthread_create( &tid[i], NULL, readFile,(void *)&info );
sleep(1);
i++
}
else if ( S_ISDIR( buf.st_mode ) )
{
// printf( " -- directory\n" );
// fflush(stdout);
}
else
{
// printf( " -- other file\n" );
// fflush(stdout);
}
}
closedir(dir);
printf("MAIN: Closed \"%s\" directory\n", argv[1]);
fflush(stdout);
printf("MAIN: Created \"%s\" output file\n",argv[3]);
fflush(stdout);
//-----------------------------------------------------------------------
for( int j = 0; j<i; j++){
printf( "MAIN: Joined child thread: %u\n", (unsigned int)tid[j] );
pthread_join(tid[i], NULL);
}
for( unsigned int i = 0; i<index_; i++ ){
int rc = fwrite( words[i], 1, sizeof(words[i]), outputfd );
if( rc == -1 ){
fprintf(stderr, "ERRPR:<write() failed>\n");
return EXIT_FAILURE;
}
}
printf( "MAIN: All threads are done; writing %d words to output file\n", index_);
fflush(stdout);
free( words );
fclose( outputfd );
return EXIT_SUCCESS;
}
This here is the whole program, and there is a header file which is just two global variab
char ** words = NULL;
/* global/shared integer specifying the size */
/* of the words array (from argv[2]) */
int maxwords;
Thanks to everyone for the help!
You need separate info objects for each thread. Right now, all of the threads get the same info object, which you change in between creating threads, and therefore, for most of them, by the time they get a chance to look at the name of the file they are supposed to process, it has been changed.
The segmentation fault is being caused by code you have not shown us, so I can't help you with that except to suggest that you apply valgrind.
Here are two more bugs:
char fileName[80];
strncat(fileName, (info->inputfile), 79);
You can only concatenate onto a string, not an unitialized array of characters that may or may not contain a valid string.
char ch[1] = {0};
char word[80] = {0};
ch[0] = fgetc(fd);
pthread_mutex_lock(&mutex);
while( ch[0] != EOF){
The fgets function returns an integer that will be EOF on end of file, otherwise it returns the character value. You convert it to a char and then compare the char to EOF. But that makes no sense since EOF is the integer value that represents end of file. Once cast to a character, it is a valid character that could have been read from the file since the file can contain any characters and "end of file" is not a character.
I'm trying to write a program which read output of another program and write to the program as input.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char str[30];
printf("Input string : ");
fflush(stdout);
scanf("%s", &str);
fflush(stdout);
printf("entered string is %s\n", str);
return 0;
}
This program1 is a simple program reading input from stdin and print the string entered.
And here in the program2, I tried to create 2 pipes and execute the program1.
And read the output of program1 and get user input and deliver the string user entered to program1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
typedef struct pipe_rw
{
pid_t cpid;
int pipe_r[2];
int pipe_w[2];
} RWPIPE;
char *get_user_input(void)
{
char buf[128];
char *input;
char ch;
int n;
int len = 0;
memset(buf, 0x0, 128);
while((ch = fgetc(stdin)) != 0xa)
{
buf[len] = ch;
len++;
}
input = malloc(sizeof(char) * (len));
strncpy(input, buf, (len));
return input;
}
int pclose_rw(RWPIPE *rwp)
{
int status, ret = 0;
if (rwp)
{
if (rwp->cpid > 0)
{
kill(rwp->cpid, SIGTERM);
do {
ret = waitpid(rwp->cpid, &status, WUNTRACED|WCONTINUED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
close(rwp->pipe_r[0]);
close(rwp->pipe_w[1]);
free(rwp);
}
return ret;
}
RWPIPE *popen_rw(const char *command)
{
RWPIPE *rwp = (RWPIPE *)malloc(sizeof(*rwp));
if (rwp == NULL)
return NULL;
memset(rwp, 0x00, sizeof(*rwp));
if (pipe(rwp->pipe_r) != 0 || pipe(rwp->pipe_w) != 0)
{
free(rwp);
return NULL;
}
rwp->cpid = fork();
if (rwp->cpid == -1)
{
free(rwp);
return NULL;
}
if (rwp->cpid == 0)
{
dup2(rwp->pipe_w[0], STDIN_FILENO);
dup2(rwp->pipe_r[1], STDOUT_FILENO);
close(rwp->pipe_r[0]);
close(rwp->pipe_r[1]);
close(rwp->pipe_w[0]);
close(rwp->pipe_w[1]);
execl(command, command, NULL);
printf("Error: fail to exec command - %s ..\n", command);
exit (1);
}
else
{
close(rwp->pipe_r[1]);
close(rwp->pipe_w[0]);
}
return rwp;
}
ssize_t read_p(RWPIPE *rwp, void *buf, size_t count)
{
return read(rwp->pipe_r[0], buf, count);
}
ssize_t write_p(RWPIPE *rwp, const void *buf, size_t count)
{
return write(rwp->pipe_w[1], buf, count);
}
int main(void)
{
char rbuf[BUFSIZ], wbuf[BUFSIZ];
int ret, len, n = 0;
char *string;
RWPIPE *rwp = popen_rw("./read_write");
if (rwp == NULL)
{
printf("Error: fail to open command ..\n");
return EXIT_FAILURE;
}
while (1)
{
memset(rbuf, 0x00, sizeof(rbuf));
if (read_p(rwp, rbuf, sizeof(rbuf)) < 1)
{
printf("No more input..\n");
break;
}
printf("%s", rbuf);
string = get_user_input();
len = strlen(string);
ret = write_p(rwp, string, len);
if (ret != len)
{
printf("Write %d bytes (expected %d) ..\n", ret, len);
break;
}
printf("end");
}
pclose_rw(rwp);
return EXIT_SUCCESS;
}
If run the program2 reads output of program1 successfully.
And it gets user input but it failed to give the string entered from user to program1.
[root#localhost test_code]# ./rw_pipe
Input string : 1234
^C
Please give me some ideas why it works like this.
Your primary problem is that the data written to the child does not end with a newline, so the child is not aware that the message is complete (it isn't complete) and the child is still busy reading while the parent is waiting for a response — a deadlock.
This code adds some instrumentation and fixes the problem by including the newline in the string read by get_input().
The original program expects two lots of input (one in response to the prompt from read_write, the other in response to the echoed output), but dies from a SIGPIPE when it tries to send the second input to the now-exited child. The code below circumvents that by ignoring SIGPIPE signals, which means that the parent gets a write error instead of being killed by the signal.
There's an unusual control flow between the two programs, and if you made read_write into an iterative program, you'd see that it generates two outputs for a single input. That's not the way it's usually done, of course. Fixing that is outside of the scope of the immediate exercise, though.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
typedef struct pipe_rw
{
pid_t cpid;
int pipe_r[2];
int pipe_w[2];
} RWPIPE;
static char *get_user_input(void)
{
char buf[128];
char *input;
char ch;
size_t len = 0;
while((ch = fgetc(stdin)) != '\n' && ch != EOF && len < sizeof(buf) - 2)
buf[len++] = ch;
buf[len++] = '\n';
buf[len] = '\0';
input = malloc(sizeof(char) * (len + 1));
strncpy(input, buf, (len + 1));
printf("Got: [%s]\n", input);
return input;
}
static int pclose_rw(RWPIPE *rwp)
{
int status, ret = 0;
if (rwp)
{
if (rwp->cpid > 0)
{
kill(rwp->cpid, SIGTERM);
do {
ret = waitpid(rwp->cpid, &status, WUNTRACED|WCONTINUED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
close(rwp->pipe_r[0]);
close(rwp->pipe_w[1]);
free(rwp);
}
return ret;
}
static RWPIPE *popen_rw(const char *command)
{
RWPIPE *rwp = (RWPIPE *)malloc(sizeof(*rwp));
if (rwp == NULL)
return NULL;
memset(rwp, 0x00, sizeof(*rwp));
if (pipe(rwp->pipe_r) != 0 || pipe(rwp->pipe_w) != 0)
{
free(rwp);
return NULL;
}
rwp->cpid = fork();
if (rwp->cpid == -1)
{
free(rwp);
return NULL;
}
if (rwp->cpid == 0)
{
dup2(rwp->pipe_w[0], STDIN_FILENO);
dup2(rwp->pipe_r[1], STDOUT_FILENO);
close(rwp->pipe_r[0]);
close(rwp->pipe_r[1]);
close(rwp->pipe_w[0]);
close(rwp->pipe_w[1]);
execl(command, command, NULL);
fprintf(stderr, "Error: fail to exec command '%s'.\n", command);
exit (1);
}
else
{
close(rwp->pipe_r[1]);
close(rwp->pipe_w[0]);
}
return rwp;
}
static ssize_t read_p(RWPIPE *rwp, void *buf, size_t count)
{
return read(rwp->pipe_r[0], buf, count);
}
static ssize_t write_p(RWPIPE *rwp, const void *buf, size_t count)
{
return write(rwp->pipe_w[1], buf, count);
}
int main(void)
{
char rbuf[BUFSIZ];
int ret, len;
char *string;
signal(SIGPIPE, SIG_IGN);
RWPIPE *rwp = popen_rw("./read_write");
if (rwp == NULL)
{
printf("Error: fail to open command ..\n");
return EXIT_FAILURE;
}
while (1)
{
memset(rbuf, 0x00, sizeof(rbuf));
if (read_p(rwp, rbuf, sizeof(rbuf)) <= 0)
{
printf("No more input..\n");
break;
}
printf("From child: [%s]\n", rbuf);
string = get_user_input();
len = strlen(string);
printf("Length %d: [%s]\n", len, string);
ret = write_p(rwp, string, len);
if (ret != len)
{
fprintf(stderr, "Write %d bytes (expected %d) ..\n", ret, len);
break;
}
printf("end cycle\n");
}
printf("End of loop\n");
pclose_rw(rwp);
return EXIT_SUCCESS;
}
Sample run
The program is rwpipe53; the input I typed was Ocelot and Grumble.
$ ./rwpipe53
From child: [Input string : ]
Ocelot
Got: [Ocelot
]
Length 7: [Ocelot
]
end cycle
From child: [entered string is Ocelot
]
Grumble
Got: [Grumble
]
Length 8: [Grumble
]
Write -1 bytes (expected 8) ..
End of loop
$
Note how the square brackets (any pair of marker symbols can be used if you prefer) shows where the data starts and ends. I find that a valuable technique when debugging code.
There are two errors that show up:
main.c:80: warning: ‘main’ is normally a non-static function
main.c:88: error: expected declaration or statement at end of input
and I cant't seem to find the problem... There number of curly braces is equal... What seems to be the problem?
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include "main-getopt.h"
void print_usage_and_abort( const char *message )
{
if( NULL != message )
fprintf( stderr, "Error: %s\n", message );
fprintf( stderr, "Usage: partitioner -n <nodes> [ -f <basename> ]\n\n" );
exit( -1 );
}
void parsing (int argc, char **argv, struct Params *params)
{
char error_message[256];
params->nodes = 0;
memcpy( params->filename_base, "output", strlen("output") + 1 );
int opt;
size_t len;
int numarg;
while ((opt = getopt(argc, argv, "n:f:")) != -1) {
int i;
for (i = 1; i < argc; i++)
{
if (argv[i][0] == '-')
{
if (i+1 == argc || argv[i+1][0] == '-')
{
sprintf( error_message, "No Filename");
print_usage_and_abort( error_message );
}
if (argv[i][1] == 'n')
{
numarg = atoi( optarg );
if( numarg < 1 || numarg > 2048 )
{
sprintf( error_message, "Number of nodes agrument expects a number between 1 and 2048, actual %s", optarg );
print_usage_and_abort( error_message );
}
}
else if (argv[i][1] == 'f')
len = strlen( optarg );
// limit to buffer capacity
if( len >= MAX_FILENAME_BASE )
{
sprintf( error_message, "Base filename parameter length is expected to be less than %d but is %d", (int)MAX_FILENAME_BASE, (int)len );
print_usage_and_abort( error_message );
}
else if(len<MAX_FILENAME_BASE)
{
memcpy( params->filename_base, optarg, len + 1 );
break;
}
else
{
sprintf( error_message, "Unknown command switch %c", (char)optopt );
print_usage_and_abort( error_message );
break;
}
}
}
if( 0==params->nodes )
{
sprintf( error_message, "Number of nodes switch -n is required" );
print_usage_and_abort( error_message );
}
}
int main(int argc, char *argv[])
{
struct Params params;
parse_arguments( argc, argv, ¶ms );
fprintf( stdout, "Parameters are:\n\tNumber of nodes:\t%d\n\tFilename base:\t%s\n\n", params.nodes, params.filename_base );
return 0;
}
I've edited indentation for you. Do you see now that somewhere in parsing function you've missed one closing bracket?
You didn't closing bracket '}' at the end of the parsing function.
Although my program works correctly in all cases, it doesn't use a pipe to connect the output of the first of two commands to the second when they're separated by a pipe symbol. I wrote the output of the first command to a file, then redirected the standard input of the second command to the file when the process to run that command was run. I need to use a pipe system call to create the pipe and obtain the file descriptors
for the pipe, and then run the two processes at the same time. It is a homework question and I have done 99% of the work but somehow am not able to get the pipe system call working... what I've been trying is that for an input like: Command 1 | Command 2
inside the child process for command 2 I close FD[0] then dup FD[1] and for command 1 close FD[1] then dup FD[1] and close FD[0].
I am hell confused with the file descriptors when using pipe.... I have to use a pipe
Any sort of help is appreciated. Execute function is where I am forking the processes.
Here's my code...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <regex.h>
/* Global Variables */
extern char **environ; /* Environmental Variables */
char *pathList[10]; /* List of paths from the $PATH */
int pathCount; /* Count of the # of paths in $PATH */
char *pathSet; /* Variable through which $PATH is retrieved */
int hasPipe = 0;
int cmdNo = 0;
/* This function takes the 'finalPath', the full path to executable,argList[],the
full command-line input arguments and argCount, the number of arguments from
command-line as input. It the creates a child process, in turn invokes the
execve() that finally executes the executable in 'finalPath' with the arguments
in 'argText' all stored into the args[] appropriately. Child process also handles
input and output file re-direction.
*/
void execute(char *finalPath, char *argList[], int argCount)
{
int k,fd,ofound,pos,i; /* flags and temporary variables */
pid_t pid; /* process ID */
int status, which;
char msg[100];
char *args[4]; /* argument list for execve() */
int spCase = 0;
ofound = 0;
pos=0;
pid = fork(); /* Creating a new process using fork() */
if (pid == -1) /* Checking for errors in process creation */
{
write(1,"Fork failed.\n",12);
exit(1);
}
/**************************
Checking for parent process
***************************/
if (pid != 0)
{
which = wait(&status);
if (which == -1)
{
write(1,"Wait failed.\n",12);
exit(1);
}
if (status & 0xff)
{ /* Case of abnormal termination */
sprintf(msg,"ERROR: <dShell> # process %d terminated abnormally for reason %d\n",which, status & 0xff);
write(1,msg,strlen(msg));
}
else
{ /* Case of normal termination */
sprintf(msg,"process %d terminated normally with status %d\n",which, (status >> 8) & 0xff);
write(1,msg,strlen(msg));
}
}
/*************************
Checking for child process
**************************/
if (pid == 0)
{
char argText[50];
argText[0] = '\0';
int std_fd;
if(cmdNo==0 && hasPipe)
{
close(1);
std_fd = open("temp.out", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
dup(std_fd);
}
else if(cmdNo==1 && hasPipe)
{
close(0);
std_fd = open("temp.out", O_RDONLY);
dup(std_fd);
}
/* Finding the first re-direction operator */
for( i = 0; i < argCount ; ++i)
{
if( ofound != 1 && ofound != 2)
{
if( strcmp(argList[i],"<") == 0 )
{
fd = open(argList[i+1],O_RDONLY);
if (fd < 0)
{
sprintf(msg,"ERROR: %s could not be opened\n", argList[i+1]);
write(1, msg, strlen(msg));
exit(5);
}
ofound = 1;
strcpy(argText,"\0");
close(0);
dup(fd);
close(fd);
}
else if(strcmp(argList[i],">") == 0)
{
fd = open(argList[i+1],O_CREAT | O_WRONLY, 0777);
pos = i;
ofound = 2;
strcpy(argText,"\0");
if (fd < 0)
{
sprintf(msg,"ERROR: %s could not be opened\n", argList[i+1]);
write(1, msg, strlen(msg));
exit(5);
}
close(1);
dup(fd);
close(fd);
}
}
}
/* If input re-direction operator is found check for an output re-direction along with it */
if(ofound == 1)
{
for( k = 0; k < argCount && ofound != 2; ++k)
{
if( strcmp(argList[k],">") == 0 )
{
fd = open(argList[k+1],O_CREAT | O_WRONLY , 0777);
spCase = 1;
ofound = 2;
strcpy(argText,"\0");
if (fd < 0)
{
sprintf(msg,"ERROR: %s could not be opened\n", argList[k+1]);
write(1, msg, strlen(msg));
exit(5);
}
close(1);
dup(fd);
close(fd);
}
}
}
/* If the re-direction operators are not found */
if( ofound == 0 )
{
for(i = 1; i < argCount; ++i)
{
strcat(argText, argList[i]);
strcat(argText, " ");
}
spCase = 2;
}
/* Case when both arguments and output re-direction operators are found */
if (spCase == 0)
{
if(pos == 0)
{
for( i = 3; i<argCount; ++i)
{
strcat(argText, argList[i]);
strcat(argText," ");
}
}
if(pos == argCount - 2)
{
for( i = 1; i<argCount - 2; ++i)
{
strcat(argText, argList[i]);
strcat(argText," ");
}
}
}
argText[strlen(argText)-1] = '\0'; /*because I added an extra space so trimming that*/
/* Running the execve */
args[0] = finalPath;
if(strlen(argText) == 0) /* checking if argText is populated */
{
args[1] = NULL;
}
else
{
args[1] = argText;
args[2] = NULL;
}
/* Execute command,if it returns that means it failed and need to display error and exit */
execve(args[0], args, environ);
sprintf(msg, "ERROR! execve() failed");
write(1, msg, strlen(msg));
}
}
/*******************************************************************************
This function checks if the path is accessible and continues to execute the
command. If the path does not exist of is not accessible, variable 'retFlag'
is used to return 0 to the calling function.
********************************************************************************/
int checkPath(char *exepath, char *argList[], int argCount, int flag)
{
char *finalPath;
int retFlag = flag;
if(access(exepath,X_OK) == 0)
{
finalPath = exepath;
retFlag = 1;
execute(finalPath,argList,argCount);
return retFlag;
}
else
return retFlag;
}
/**********************************************************************************
This function checks if the first argument is a path and if so calls checkPath().
Else it gets the paths set to the $PATH variable, tokenizes it, pads it with the
first token of input command and calls checkPath(). If the correct path is established,
the variable 'found' is used to kick out of the for loop.
************************************************************************************/
void setPath(char *argList[], int argCount)
{
char *exepath;
char com[50];
char emsg[80];
char *command;
int i,found = 0;
/* Seperating the command if redirection is used */
if( strcmp(argList[0],"<") == 0 || strcmp(argList[0],">") == 0 )
{
command = argList[2];
}
else
command = argList[0];
/* In case of no redirection, storing the commands and arguments into a array */
if(strcmp(command,"#") == 0) /* Checking for comment statements */
{
write(1,"ERROR: No command(s) found. Only comment present/n",48);
}
else
{
if(strstr(command,"/")) /* Checking if the entire path is given as a part of the command */
{
exepath = command;
found = checkPath(exepath,argList,argCount,0);
}
else /* building the path and storing it in 'com' */
{
for(i = 0; i< pathCount && found != 1; i++)
{
sprintf(com,"%s%s%s",pathList[i],"/",command);
exepath = com;
found = checkPath(exepath,argList,argCount,0);
}
}
if(found == 0)
{
sprintf(emsg,"%s%s",command,":COMMAND DOES NOT EXIST");
write(1,emsg,sizeof(emsg));
write(1,"\n",1);
}
}
}
/* Tokenizes commands into words */
void tokens(char *cmdStr)
{
char cmd[100];
strcpy(cmd,cmdStr);
char *result;
char delims[] = " , ";
char *argList[20];
int argCount = 0;
/*Tokenize the individual command into strings */
result = strtok(cmd,delims);
while( result != NULL )
{
argList[argCount] = result;
result = strtok( NULL, delims );
++argCount;
}
setPath(argList,argCount);
}
/* Tokenizes multiple commands into single commands */
void tokenize(char *inputStr)
{
int i,cmdCount = 0;
char *cmdResult;
char *cmdStr[100];
char delimiters[] = "|";
cmdResult = strtok(inputStr, delimiters);
while(cmdResult != NULL)
{
cmdStr[cmdCount]=cmdResult;
cmdResult = strtok(NULL, delimiters);
cmdCount++;
}
if( cmdCount > 1 )
hasPipe = 1;
else
hasPipe = 0;
for( i=0; i<cmdCount ; i++)
{
cmdNo = i%cmdCount;
tokens(cmdStr[i]);
}
}
int main(int argc, char *argv[])
{
char prompt[8]; /* String that stores the personalized prompt */
char *path; /* Temporary variable used for tokenization*/
char ch; /* Temporary variable used in read() */
int chCount; /* # of characters read from the prompt */
int entry; /* return variable of read() */
int flag; /* Flag to go read the next command when newline is found */
regex_t reIgnore;
char pattern[20]="^\\s*$|^#.*";
/* Tokenizing the paths asociated with the $PATH and storing them in a array declared globally */
pathCount = 0;
pathSet = getenv("PATH");
if ( !pathSet)
{
write(1, "ERROR: PATH environment does not exist.\n", 40);
exit(1);
}
path = strtok(pathSet,":");
while(path != NULL)
{
pathList[pathCount] = path;
path = strtok(NULL,":");
++pathCount;
}
/* Checks for blanks and tabs in Step 2 */
if ( regcomp(&reIgnore, pattern, REG_EXTENDED) )
{
write(1, "Error. \n",9);
exit(2);
}
sprintf(prompt,"<dShell> # "); /* Storing the personalized shell prompt into 'prompt' */
/* Reading the input from command line and passing it to tokenize() */
while(1)
{
char inputStr[100]; /* String into which inputs are read into */
chCount = 0;
flag = 0;
hasPipe = 1;
write(1,prompt,strlen(prompt)); /* Printing out the personalized shell prompt */
/* This will read a character 1 by 1 until it reaches the end of file */
entry = read(0,&ch,1);
if(!entry)
exit(0);
/* Reading the input and storing it in inputStr as long as newline is not encountered */
while( entry != 0 && flag == 0 )
{
/* A newline has been found so a new command will need to be executed */
/* The inputStr till this point is sent to tokenize() */
if( ch == '\n' )
{
inputStr[chCount] = '\0';
flag = 1;
if(chCount > 0) {
if(strcmp(inputStr,"exit") == 0)
exit(3);
else
tokenize(inputStr);
}
}
inputStr[chCount] = ch;
chCount++;
if(flag == 0)
entry = read( 0, &ch, 1 );
}
}
}
See the man page for pipe(2). It has this example:
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
assert(argc == 2);
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}