So to start off, this program has two main parts. The first takes input from the command line using fgets and then makes an argv array with a function called makeargv. The second, takes that argv array and runs it using execvp. The problem I am having is that the program will only run system programs such as "ls", "pwd", "vim", etc. but will not run any program when the directory is specified, such as "./program". I have already tried different versions of exec but the only difference that has made is that then my program will no longer run any commands.
For the below program I cut out all the code that was not relevant to the question to avoid confusion.
#ifndef MAX_CANON
#define MAX_CANON 8192
#endif
int makeargv(const char *s, const char *delimiters, char ***argvp);
int main (int argc, char *argv[]) {
char cmd[MAX_CANON];
char delim[] = "\t";
char **myargv;
printf("Beginning program...\nEnter a command to execute:\n");
while(fgets(cmd, MAX_CANON, stdin) != NULL){ // Here's where I get input from the command line
/* Remove newline from end of command */
if (*(cmd + strlen(cmd) - 1) == '\n' || *(cmd + strlen(cmd) - 1) == ' ' )
*(cmd + strlen(cmd) - 1) = 0;
/*---- Child Code ----*/
if((p = fork()) == 0){
if (makeargv(cmd, delim, &myargv) == -1) { // Here is where I make the argv array
fprintf(stderr, "Child failed to construct an argument array for %s\n", &cmd[0]);
return 1;
}
fprintf(stderr, "Command is: %s\n", *&myargv[0]);
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
fprintf(stderr, "Error: Failed to execute command!\n");
return 1;
}
return 0;
}
/*---- Parent Code ----*/
Here is the makeargv code
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int makeargv(const char *s, const char *delimiters, char ***argvp) {
int error;
int i;
int numtokens;
const char *snew;
char *t;
if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) {
errno = EINVAL;
return -1;
}
*argvp = NULL;
snew = s + strspn(s, delimiters); /* snew is real start of string */
if ((t = malloc(strlen(snew) + 1)) == NULL)
return -1;
strcpy(t, snew);
numtokens = 0;
if (strtok(t, delimiters) != NULL) /* count the number of tokens in s */
for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ;
/* create argument array for ptrs to the tokens */
if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) {
error = errno;
free(t);
errno = error;
return -1;
}
/* insert pointers to tokens into the argument array */
if (numtokens == 0)
free(t);
else {
strcpy(t, snew);
**argvp = strtok(t, delimiters);
for (i = 1; i < numtokens; i++)
*((*argvp) + i) = strtok(NULL, delimiters);
}
*((*argvp) + numtokens) = NULL; /* put in final NULL pointer */
return numtokens;
}
Edit:
Swapped fprintf for perror.
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
fprintf(stderr, "Error: Failed to execute command!\n");
return 1;
}
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
perror("Error: Failed to execute command!\n");
return 1;
}
I am now getting a "No such file or directory" error.
FIXED:
The makeargv program was using "\t" as it's delimeter instead of " " so it was not creating the array correctly.
Changing:
char delim[] = "\t";
To:
char delim[] = " ";
Fixes the problem.
There is 2 type of exec:
the first need a path as the environment PATH who contain a location (where find sys executables)
and the others how take real file location.
Form the man page (https://linux.die.net/man/3/exec):
The execlp(), execvp(), and execvpe() functions duplicate the actions of the shell in searching for an executable file if the specified filename does not contain a slash (/) character.
So this mean that you need to use an absolute location, who always begin with /.
You can use getwd() and strcat() to concatenate the 2 strings.
For execvp:
int execvp(const char *file, char *const argv[]); // From the man (I <3 mans)
So:
char argv[3][] = { "/bin/ls", "/", NULL};
if (execvp("/bin/ls", argv) == -1)
return (1);
For evecv:
int execv(const char *path, char *const argv[]);
There is a problem, it need the system PATH. If you don't know what it is type "echo $PATH" in bash. It's a list of directories where the system can find binaries like "ls" in "/bin", concatenate with ":" as sparator. I find the exact definition here.
For your makeargv:
I don't understand why you deference it and take the address after.
execvp(myargv[0], &myargv[0])
char **myargv; //That's we have
char *const argv[]; //that's we need
//so you should try simply:
execvp(myargv[0], myargv);
//or if there is a cast problem
execvp(myargv[0], (char *const[])myargv);
Good luck!
When I run my code the first printParams() call works perfectly. But every call after fork() the struct loses all of its char array values. I'm not that knowledgeable of pointers, but I can tell that the root of this problem is probably pointer-based. For example, the first printParams() will print out all of the values assigned in the Parse() function. But after the fork(), all of the integer values such as background and argumentCount are displayed but none of the string values associated with inputRedirect or the string values held in the vectorArguments array.
![Here is a photo of my output]]1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "parse.h"
void printParams(Param_t * param);
struct PARAM
{
char *inputRedirect; /* file name or NULL */
char *outputRedirect; /* file name or NULL */
int background; /* ethier 0 (false) or 1 (true) */
int argumentCount; /* number of tokens in argument vector
*/
char *argumentVector[MAXARGS]; /* array of String */
};
typedef struct PARAM Param_t;
int main(int argc, char *argv[]){
int i;
int debug;
pid_t pid;
if(argc>1){
if(!strcmp(argv[1], "-debug"))
debug = 1;
}
Param_t * testParam = Parse();
if(debug == 1){
printParams(testParam);
}
pid = fork();
printParams(testParam);
if(pid == 0){
exit(1);
}
return 0;
}
void printParams(Param_t *param)
{
int i;
printf("InputRedirect: [%s]\n", (param->inputRedirect != NULL) ? param-
>inputRedirect: "NULL");
printf("OutputRedirect: [%s]\n", (param->outputRedirect != NULL) ?
param->outputRedirect: "NULL");
printf ("Background: [%d]\n", param->background);
printf ("ArgumentCount: [%d]\n", param->argumentCount);
for (i = 0; i < param->argumentCount; i++)
printf("ArgumentVector[%2d]: [%s]\n", i, param->argumentVector[i]);
}
Param_t* Parse(){
char *toke[MAXARGS];
int i = 0;
char str[MAXSTRLENGTH];
int j;
int k=0;
Param_t* testParam = malloc(sizeof(Param_t));
testParam->argumentCount = 0;
printf("Enter your commands:\n");
fgets(str, MAXSTRLENGTH, stdin);
toke[i] = strtok(str, " ");
//Tokenizes the user input into the toke array
while(toke[i] != NULL){
//printf("%s\n", toke[i]);
++i;
toke[i] = strtok(NULL, " ");
}
i=0;
char c;
while(toke[i] != NULL){
c = toke[i][0];
if(c == '<')
{
for(j=0; j<strlen(toke[i]); ++j ){
toke[i][j] = toke[i][j+1];
}
testParam->inputRedirect = toke[i];
}
else if(c == '>')
{
for(j=0; j<strlen(toke[i]); ++j ){
toke[i][j] = toke[i][j+1];
}
testParam->outputRedirect = toke[i];
}
else if(c == '&')
{
testParam->background = 1;
//background
}
else
{
testParam->argumentVector[k] = toke[i];
k++;
//save as cmd vector
}
++i;
}
testParam->argumentCount = k;
return testParam;
}
The reason you loose all char * values is because strtok() function doesn't create buffer. Basically all your char* consist of an address pointing into str variable you read with fgets(). The str variable has scope only to end of Parse() function.
Solution for this:
Replace:
testParam->inputRedirect = toke[i];
with:
testParam->inputRedirect = malloc(MAXSTRLENGTH);
memset( testParam->inputRedirect, 0, MAXSTRLENGTH);
memcpy( testParam->inputRedirect, toke[i], strlen(toke[i]) );
But please note, that this leads to memory leak since there is no free().
Sugestion:
Create static instance of structure in main and give its pointer to Parse function.
Param_t testParam;
Parse( &testParam );
Let the Parse function fill it. And at the end of main call free for all char * buffers inside testParam
I need split data from ethernet. Data is in this format:
ZMXXX,angle*CHCK
Where angle is number. For example: ZMXXX,900*5A
And I need separated ZMXXX,900 and 5A. I wrote this function:
void split_data(char analyze[])
{
char *words[5]; uint8_t i=0;
words[i] = strtok(analyze,"*");
while(words[i]!=NULL)
{
words[++i] = strtok(NULL,"*");
}
}
And result is here:
And now, how I can get this data from variable:
words[0]
words[1]
Assuming the format you mention to be fixed, there is no need for the expensive and error-prone strtok().
Use the good old strchr():
int parse(char * input, char ** output)
{
int result = -1; /* Be pessimistic. */
if ((NULL == inout) || (NULL == output))
{
errno = EINVAL;
goto lblExit;
}
char * pc = strchr(analyze, '*');
if (NULL == pc);
{
errno = EINVAL;
goto lblExit;
}
*pc = '\0'; /* Set a temporary `0`-terminator. */
output[0] = strdup(analyze); /* Copy the 1st token. */
if (NULL == output[0])
{
goto lblExit;
}
*pc = '*'; /* Restore the original. */
output[1] = strdup(pc + 1); /* Seek beyond the `*` and copy the 2nd token. */
if (NULL == output[1])
{
free(outout[0]); /** Clean up. */
goto lblExit;
}
result = 0; /* Indicate success. */
lblExit:
return result;
}
Use it like this:
#define _POSIX_C_SOURCE 200809L /* To make strdup() available. */
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int parse(char *, char **);
int main(void)
{
char data[] = "ZMXXX,900*5A";
char * words[2];
if (-1 == parse(data, words))
{
perror("parse() failed");
exit(EXIT_FAILURE);
}
printf("word 1 = '%s'\n", words[0]);
printf("word 2 = '%s'\n", words[1]);
free(words[0]);
free(words[1]);
return EXIT_SUCCESS;
}
The above code is expected to print:
word 1 = 'ZMXXX,900'
word 2 = '5A'
Note that strdup() isn't Standard C, but POSIX. It might need to be activated using one of the appropriate defines.
I'm trying to implement a basic shell, I have several functions within it that deal with strings, trying to find file names, implement something equivalent to *argv[] and so on.
I have strings in main(), which are passed to a function to be populated. Next the program returns to main(), which passes the strings to another function to be acted upon.
I was debugging with lldb and found that I was successfully populating the strings with the correct values in the first function but upon exiting the function, re-entering main() the output_str string was NULL again. I thought strings, since they point to space in memory would retain values. They seem to for all but one case, when flag = 1 in the code below.
I can't figure out what's happening as the values seem to only be lost after the final } of the function.
Edited to add complete code, hope it isn't too large.
The code works with say, cat input.txt but not with cat input.txt>output.txt when I try to redirect the output from stdout to a file
Thank you for your help in advance.
Here is the function .c file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
void sig_handler(int signo)
{
if (signo == SIGINT)
{
fprintf(stdout, "\n");
fflush(stdout);
}
}
int check_redirect(char *line, int flag)
{
int n = 0;
if (line == NULL) return (flag);
else
{
do
{
if (line[n] == '>') flag = 1;
n++;
}while (line[n] != '\0');
}
return (flag);
}
void string_breakdown(char *line, char **output_str, int count, char* temp, char *filename, int *f, int *saved_stdout, int flag, int debug)
{
char *sep = " \n";
char *delim = ">\n";
if (line != NULL)
{
temp = strtok(line, delim);
while (temp != NULL)
{
output_str[count] = temp;
if (debug) fprintf(stderr, "1:%s\n2:%s\n3:%s\n", line, temp, output_str[count]);
count++;
output_str = realloc (output_str, (count + 1) * sizeof (char *) );
temp = strtok(NULL, delim);
}
if (flag)
{
count = 0;
strcpy(filename, output_str[1]);
output_str[1] = NULL;
*saved_stdout = dup(1);
*f = open(filename , O_WRONLY|O_CREAT|O_TRUNC, 0666);
dup2(*f, 1);
temp = strtok(*output_str[0], sep);
while (temp != NULL)
{
output_str[count] = temp;
//if (debug) fprintf(stderr, "1:%s\n2:%s\n3:%s\n", line, temp, output_str[count]);
count++;
output_str = realloc (output_str, (count + 1) * sizeof (char *));
temp = strtok(NULL, sep);
}
}
else
{
count = 0;
temp = strtok(line, sep);
while (temp != NULL)
{
output_str[count] = temp;
if (debug) fprintf(stderr, "1:%s\n2:%s\n3:%s\n", line, temp, output_str[count]);
count++;
output_str = realloc (output_str, (count + 1) * sizeof (char *));
temp = strtok(NULL, sep);
}
}
}
}
void com_exec(char *line, char **output_str, char *filename, int *f, int *saved_stdout, int flag, int debug)
{
char *command = malloc(sizeof(char *));
command = output_str[0];
char *name = "HOME";
int ret_val = 0;
pid_t child_pid;
int child_status;
if (command == NULL);
else if (strcmp("cd", command) == 0)
{
if (output_str[1] == NULL) output_str[1] = getenv(name);
ret_val = 0;
ret_val = chdir(output_str[1]);
if (ret_val) perror(NULL);
}
else
{
child_pid = fork ();
if (child_pid == 0)
{
if (debug)
{
system(line);
fprintf(stderr, "Post System Pre Exec\n1:%s\n2:%s\n3:%s\n", line, output_str[0], command);
sleep(2);
}
execvp(command, output_str);
if (flag)
{
close(*f);
dup2(*saved_stdout, 1);
close(*saved_stdout);
}
fprintf (stdout, "Unknown command\n");
exit (0);
}
else
{
if (flag)
{
close(*f);
dup2(*saved_stdout, 1);
close(*saved_stdout);
}
signal(SIGINT, sig_handler);
usleep(500000);
//Parent process waits for child to finish
if (debug) fprintf (stderr, "parent waiting\n");
wait(&child_status);
waitpid(child_pid, &child_status, 0);
signal(SIGINT, SIG_DFL);
}
}
Here is the functions .h file:
#ifndef SHELL_H_INCLUDED
#define SHELL_H_INCLUDED
void sig_handler(int signo);
int prompt(char *line, size_t len, ssize_t read);
int check_redirect(char *line, int flag);
void string_breakdown(char *line, char **output_str, int count, char* temp, char *filename, int *f, int *saved_stdout, int flag, int debug);
void com_exec(char *line, char **output_str, char *filename, int *f, int *saved_stdout, int flag, int debug);
#endif // LINKLAYER_H_INCLUDED
Below is main.c, where the function is called.
#include <unistd.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include "shell.h"
int main(void)
{
int debug = 0;
char *line = NULL;
size_t len = 0;
ssize_t read = 0;
int flag = 0;
int f = 0;
int saved_stdout = 0;
do
{
flag = 0;
//read = prompt(line, len, read);
char buffer[15];
time_t now = time(NULL);
strftime(buffer, 15, "[%d/%m %H:%M]", localtime(&now) );
fprintf(stdout, "%s # ", buffer);
signal(SIGINT, SIG_IGN);
read = getline (&line, &len, stdin);
signal(SIGINT, SIG_DFL);
flag = check_redirect(line, flag);
char **output_str = malloc(sizeof(char *));
int count = 0;
char* temp = NULL;
char *filename = malloc(sizeof(char *));
string_breakdown(line, output_str, count, temp, filename, &f, &saved_stdout, flag, debug); // function call of problem function
com_exec(line, output_str, filename, &f, &saved_stdout, flag, debug);
} while (read != EOF);
if (debug) fprintf(stderr, "parent exiting\n");
else fprintf(stdout, "\n");
return 0;
}
output_str = realloc (output_str, (count + 1) * sizeof (char *) );
This line re-assigns the value of the the local parameter variable output_str, but the new value in no way makes it back to the caller of the string_breakdown function - meaning that the pointer it has will probably be left dangling, and will cause problems when used ("undefined behavior", manifesting in strange program behavior or crashing).
You need to understand that within the function, output_str is a local variable. You can change its value, but that won't affect the value of any variable in the caller.
You call the function from main:
string_breakdown(line, output_str, count, temp, filename, &f, &saved_stdout, flag, debug); // The call of the above function
main also uses output_str as the variable name, but again, this is a different variable. One variable is local to main, the other is local to string_breakdown, even though they share the same name. Due to the realloc call above, the pointer value in main's output_str will most likely be invalid on return from string_breakdown, because it is not updated to point to the newly allocated memory. That's why you are "losing" the string values on return from the function - the output_str variable in main is no longer actually pointing to the array of strings, which has been moved to a different location via realloc.
Typically you resolve this kind of problem by adding another level of indirection, changing the output_str parameter from a char ** to a char ***:
void string_breakdown(char *line, char ***output_str, int count, char* temp, char *filename, int *f, int *saved_stdout, int flag, int debug)
and
(*output_str)[count] = temp;
and
*output_str = realloc (*output_str, (count + 1) * sizeof (char *) );
and so on. You need to adjust the call in main as well:
string_breakdown(line, &output_str, count, temp, filename, &f, &saved_stdout, flag, debug); // The call of the above function
Because you are passing a pointer to main's output_str variable, the called function is now able to modify its value.
You should also understand that string_breakdown as written modifies the string which the line parameter points to. That's because it uses strtok, and strtok replaces delimiters with nul bytes as it processes the string. So, it is odd that you pass this modified line buffer to com_exec after processing it with string_breakdown.
I get several warnings when I try to compile your code; main.c uses fprintf but doesn't #include <stdio.h>, and uses malloc but doesn't #include <stdlib.h>.
your realloc does nothing.
you mean *output_ptr = realloc....
actually it does something, but its really bad
this is also wrong
output_str[count] = temp;
and this
filename = output_str[1];
you need to distinguish - a pointer to your buffer, a pointer to the pointer to your buffer.
char * buffer = *output_str; // to remove the confusion
strcpy(&buffer[count], temp); // assigning pointers doesnt copy things
filename = buffer[1]; // is hat what you mean - filename is one char
So i am attempting to pass a string array (char** arguments) to a function, fill the array with values and then print those values after returning from the function. The problem occurs when I try to print the first value of "arguments" which gives me a segmentation fault. Why is this? when I print the values in the "getArguments" function all goes as expected. I am new to C and yes this is an assignment. I am not looking for you to write this code for me however I would like an explanation of this behaviour as I try to understand this concept.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFFERSIZE 81
int getArguments(char** arguments, char* argument);
void getPath(char* pathBuffer);
int checkForDirectoryChange(char **arguments, int num_args);
int main(int argc, char *argv[]){
char * command;
char ** arguments = NULL;
char * cd_path;
int len, pid, ret_code, cd_requested = 1;
char buffer[BUFFERSIZE];
/* Get user input and the first token */
printf("Enter a command: > ");
command = fgets(buffer,BUFFERSIZE,stdin);
printf("The command entered was %s",buffer);
len = strlen(buffer);
if(buffer[len-1] == '\n')
buffer[len-1]='\0';
cd_requested = getArguments(arguments, command);
printf("The argument passed is now: %s\n", arguments[0]);
if(cd_requested == 0){
fprintf(stdout,"Change directory requested.\n");
}
/*
char * pathBuf;
getPath(pathBuf);
free the memory allocated */
/*
pid = fork();
if(pid){
wait(NULL);
}else{
ret_code = execvp(*arguments, arguments);
if(ret_code){
printf("The fork failed, exiting.");
exit(0);
}
}*/
}
int getArguments(char** arguments, char* command){
int n_spaces = 0,i;
char *token;
token = strtok(command, " ");
/* Loop until we have gotten all of the tokens */
while (token) {
arguments = realloc (arguments, sizeof (char*) * ++n_spaces);
if (arguments == NULL){
printf("Memory allocation failed: token - %d\n", n_spaces);
exit (-1); /* memory allocation failed */
}
arguments[n_spaces-1] = token;
token = strtok (NULL, " ");
}
/* realloc one extra element for the last NULL */
arguments = realloc (arguments, sizeof (char*) * (n_spaces+1));
arguments[n_spaces] = 0;
/* print the result */
for (i = 0; i < (n_spaces+1); ++i)
printf ("arguments[%d] = %s\n", i, arguments[i]);
return strcmp("cd",arguments[0]);
}
int checkForDirectoryChange(char** arguments, int num_args){
return 0;
}
void getPath(char* pathBuffer){
size_t n;
n = confstr(_CS_PATH, NULL, (size_t) 0);
pathBuffer = malloc(n);
if (pathBuffer == NULL)
abort();
confstr(_CS_PATH, pathBuffer, n);
}
It is because getArguments() only reassigned the copy of pointer to pointer of characters inside itself. arguments in main() was not updated.
You should define getArguments() as
int getArguments(char*** arguments, char* command) {
/* ... */
while (token) {
*arguments = realloc (*arguments, sizeof (char*) * ++n_spaces);
if (*arguments == NULL){
printf("Memory allocation failed: token - %d\n", n_spaces);
exit (-1); /* memory allocation failed */
}
(*arguments)[n_spaces-1] = token;
token = strtok (NULL, " ");
}
/* ... */
}
And call it as the following inside main().
cd_requested = getArguments(&arguments, command);