Problems passing in a char[][] to execvp - c

I am trying to iterate over some input (which are commands and arguments), split the input into individual strings, then pass that input into execvp().
I am having trouble as execvp() wants a (char*, char*[]) as its arguments. I am passing in (char*, char[][]) which I thought was the same thing but it isn't liking it.
I would use a char*[] but I don't know how big the strings are prior to it running, so that is the reason I didn't use it. So obviously if I use char*[], I get a seg fault when I try and access elements of the char*'s.
Here's a snippet of the code
//input
char *line = "echo hello there what is";
int count = 0;
//store arguments
char args[6][10];
int argCount = 0;
//store temp string from input
char temp[100];
int tempCount = 0;
//word(string) size
int wordSize = 0;
/*
Here I iterate over the input, storing each string I find in args
all the above variables help me do that.
*/
execvp(args[0], args);
printf("Error: It didnt work\n");
Hopefully that is clear and a valid question, let me know if you want me to add the code of me turning the input into args.

An array of arrays of char is not the same as an array of pointers to arrays of char. execvp() expects the latter, with a null pointer as its last element, passing the former has undefined behavior.
You must construct an array of pointers, either allocated from the heap or defined with automatic storage (on the stack), initialize it with pointers to argument strings and pass this array to execvp().
Note also that echo is both a shell internal command and an executable file in the path.
Here is your code fragment modified accordingly (without the parse code, which is still yours to write):
//input "echo hello there what is";
//arguments array
char *args[6];
/*
* Here you should iterate over the input, storing each string you find
* on the command line into `args` and terminate with a null pointer...
*/
args[0] = "echo";
args[1] = "hello";
args[2] = "there";
args[3] = "what";
args[4] = "is";
args[5] = NULL;
execvp(args[0], args);

You can use two arrays:
char real_args[6][10]; // Six strings of up to nine characters each
...
char *args[] = {
real_args[0],
real_args[1],
real_args[2],
real_args[3],
real_args[4],
real_args[5],
NULL // Must be terminated by a null pointer
};
Use real_args for the actual arguments, and then pass args to execvp.

Ok I worked out how to use a char*[] instead of a char[][] and successfully put it into execvp().
Notes for the code below: I keep track of how long the current string is that I am iterating over in wordSize.
//input
char* line = "echo hello there what is";
int count = 0;
//store arguments
char* args[10];
int argCount = 0;
//store temp string from input
char temp[100];
int tempCount = 0;
//word size
int wordSize = 0;
while(line[count] != '\0')
{
if (line[count] == ' ' || line[count + 1] == '\0')
{
/*
As I don't know how big each string will be, I can
allocate it here as I know how big the string is through wordSize
*/
args[argCount] = malloc(sizeof(char)*(wordSize +1));
.
.
.
}
}
//terminate args with 0
args[argCount] = 0;
execvp(args[0], args);
printf("Error: It didnt work\n");

Related

Passing parameters a a string to execvp

I have the below code:
int main(int argc, char *argv[])
{
int i, a=9;
int length = 0;
const char fail[20] = "Missing Arguments\n";
char s1[512] = "";
char s2[15] = "./calc_prizes";
for (i=1; i<argc; i++) {
length += sprintf(s1+length, " %s", argv[i]);
}
strcat(s2, s1);
while(++a < argc) {
if(fork() == 0) {
char* arg[] = {s2, s1, NULL}; //this is the part that's wrong
execvp(arg[0],arg);
exit(1);
}
else
wait(NULL);
}
return 0;
}
S2 stores the name of the program, s1 the parameters collects the parameters. I can't seem to run the program with the parameters with execvp, what am I doing wrong?
One possible reason of execvp failing could be this:
strcat(s2, s1);
[I hope you have ensured that s2 is large enough to contain the concatenated resulting string otherwise its buffer overflow, which is a different but certainly a problem in your code.]
Here you are concatenating the s1 to s2 and s2 is the name of the program you want to execute. In the while loop, you are doing:
char* arg[] = {s2, s1, NULL};
The arg[0] pointing to s2 (concatenated string) and you are passing this as the first argument to execvp:
execvp(arg[0],arg);
execvp:
The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a NULL pointer. [emphasis mine]
Hence, to call execvp successfully you should have the first argument as the name of the executable file which is ./calc_prizes in your case.

execv() system call creating stacking smashing error

Whenever I used the execv() here in my code, it works and has no errors, but still causes stack smashing to crash the program during runtime. Am I doing anything wrong here?
Here is the function with the execv():
void execute(char *args[], char *cmd[])
{
pid_t pid;
int status;
char bin[10] = "/bin/";
pid = fork();
// child process
if(pid == 0)
{
strcat(bin, cmd[0]);
execv(bin, args);
} else{
perror("error");
while(wait(&status) != pid);
}
}
here is where I am getting args and cmd from. Would it possibly be caused by something I did here?
void parseString(char *command)
{
char **args = malloc(sizeof(char*) * 16);
int i = 0;
int j = 0;
char *cmd[1];
// split each command by semicolons if necessary, then send each sub command to parseString()
if(strchr(command, ';')) {
char *semi_token = strtok(command, ";");
while(semi_token != NULL){
args[i] = semi_token;
semi_token = strtok(NULL, " ");
parseString(args[i]);
i++;
}
} else {
// if no semi colons, split the commandby spaces and call execute() using the args and cmd
char *token = strtok(command, " ");
while(token != NULL)
{
args[i] = token;
args[++i] = NULL;
while(j == 0 && token != NULL) {
cmd[0] = token;
cmd[1] = NULL;
j++;
}
token = strtok(NULL, " ");
}
execute(args, cmd);
}
j = 0;
i = 0;
free(args);
}
function call happens here. command is input from stdin from the user. Only need basic commands all located in /bin/. something like ls -l or cat file.
while(1){
command = getCommand();
parseString(command);
}
You have two serious errors: One that will lead to out-of-bounds writing of an array, and one that will probably lead to that.
The first, the certain out-of-bounds writing, is in the parseString function. First you have the declaration of the cmd variable:
char *cmd[1];
This defines cmd as an array of one element. Then you do
cmd[0] = token;
cmd[1] = NULL;
which writes to two elements of the one-element array. Writing out of bounds leads to undefined behavior.
The second error is in the execute function, and is the one I talked about in my first comment. There you have
char bin[10] = "/bin/";
That defines bin as an array of ten characters, and you fill up six of them (don't forget the string terminator). In the child-process you do
strcat(bin, cmd[0]);
which appends to string in cmd[0] to the string in bin. The problem here is that bin only have space for ten characters, of which six is already used (as explained above). That means there's only space left for four characters. If the command is any longer than that you will also go out of bounds and again have undefined behavior.
The solution to the first error is simply, make cmd an array of two elements. The solution to the second error is to either make bin larger, and not concatenate more than can fit in the array; Or to allocate the array dynamically (not forgetting space for the terminator).
There are also lot of other potential problems with your code, like the limit on 16 pointers for args. And that you don't really parse arguments in the parseString function, every argument is seen as a separate command. And the memory leak in the case there are semicolon-separated "commands". Or that you don't check for or handle errors everywhere needed. And that you use errno even if there's no error.

decrypt and encrypt text with command line arguments

I am trying to make a program that will encrypt and decrypt when user enters the string they want to encrypt/decrypt for argv[2] and enters either "encrypt" or "decrypt" for argv[3]. Here is the code I am trying to compile and run as of now
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
int i;
// char *string;
char *key_ch;
char key_int;
char *string_ = calloc(80, 1);
string = argv[1];
char encrypted_string[strlen(string)];
char decrypted_string[strlen(string)];
//char *key_ch;
//char key_int;
string = argv[1];
key_ch = argv[2];
key_int = atoi(key_ch);
if (argc < 3)
{
printf("Not enough arguments!\n");
exit (1);
}
if (strcmp(argv[3], "encrypt") == 0)
{
i = 0;
while(i <= strlen(string)-1)
{
encrypted_string[i] = string[i] + key_int;
i++;
}
// printf("Encrypted string: ");
i = 0;
while (i <= strlen(string) -1)
{
printf("%c", encrypted_string[i]);
i++;
}
printf("\n");
}
if (strcmp(argv[3], "decrypt") == 0)
{
i = 0;
while(i <= strlen(string) -1)
{
decrypted_string[i] = string[i] - key_int;
i++;
}
// printf("Decrypted String: ");
i = 0;
while (i <= strlen(string) -1)
{
printf("%c", decrypted_string[i]);
i++;
}
printf("\n");
}
return 0;
}
When I try to compile it without the -Wall command it compiles fine but when I run the program I am getting a segmentation fault, when I compile with -Wall I am getting
sam0.c:9:24: warning: 'string' is used uninitialized in this function [-Wuninitialized]
char encrypted_string[strlen(string)];
Can anyone possible shine some light on this error? Thank you
Edit:
Changed my code to your suggestions. I am not getting a compiling error at all even when using "-Wall" however somewhere in my program it is causing me to get a segmentation fault... any ideas? I put quotes around where I changed my code for reference in case I did it wrong.
warning: 'string' is used uninitialized in this function
[-Wuninitialized]
char *string; creates a pointer to char. At this point it is not yet a string, but you are using it as a string argument.
Before using char *string; it must have memory assigned, and should be initialized. Among other methoods, this can be done by:
char *string = calloc(80, 1);//initializes with known values (NULL).
Now string is usable, but has zero length. Values can be assigned via string functions:
strcpy(string, argv[1]);
sprintf(string, "%s", argv[1]);
strcat(string, argv[1]);
... more string functions
When using input from command line, argv, argc, malloc/calloc and string cpy functions can be avoided by using strdup. a value can be assigned like this:
if(argc == 2)
{
char *string = strdup(argv[1]);
if(!string) return -1;
...
EDIT (addressing your OP edit)
You are now using two different variables: string_ and string
char *string_ = calloc(80, 1);
^
string = argv[1];
Make them the same throughout your code and it should build and run.
When a meaningful value for the variable can be determined, try to declare and initialize the variable in one step:
char *string = argv[1];
char encrypted_string[strlen(string)];
char decrypted_string[strlen(string)];
char *key_ch = argv[2];
char key_int = atoi(key_ch);
This will fix the warning: you were trying to get the length of an uninitialized string.
(here assuming C99. char encrypted_string[strlen(string)] is a VLA. If you're restricted to C89 only Rykker's answer works).
Also:
use const to prevent unwanted modifications, e.g.
const char *string = argv[1];
/* ... */
const char *key_ch = argv[2]
string is a constant so you can get its length just one time (do not recalculate the length every time... take a look at Shlemiel the painter's algorithm). So
const char *string = argv[1];
const int length = strlen(string);
char encrypted_string[length];
char decrypted_string[length];
const char *key_ch = argv[2];
char key_int = atoi(key_ch);
check inputs: what happen if
argc < 4
argv[3] isn't in ("encrypt", "decrypt")
key_ch is not a number
key_int is too big
you're printing encrypted_string / decryped_string one character at a time... and it works, but consider that if you want to manipulate them as 'strings' they aren't null terminated and their length isn't correct.
don't repeat yourself. The differences between the encryption and the decryption phases are minimal: you can use just one buffer and change key_int to a negative value to decrypt.
char *string;
char encrypted_string[strlen(string)];
char decrypted_string[strlen(string)];
This results in undefined behavior - you try to get the length of an uninitialized piece of memory (I'm amazed that your program doesn't crash). You may only call strlen after assigning something to string.
The easiest fix (assuming you don't want to start using malloc, strdup, etc.) would be moving the last two lines after string = argv[1];
Also, you need to check argc to make sure enough arguments have been passed!

Double pointer to char[]

Alright, so I have the following code:
char** args = (char**)malloc(10*sizeof(char*));
memset(args, 0, sizeof(char*)*10);
char* curToken = strtok(string, ";");
for (int z = 0; curToken != NULL; z++) {
args[z] = strdup(curToken);
curToken = strtok(NULL, ";")
}
I want every arg[z] casted into an array of chars -- char string[100] -- and then processed in the algorithms I have following. Every arg[z] needs to be casted to the variable string at some point. I am confused by pointers, but I am slowly getting better at them.
EDIT:
char string[100] = "ls ; date ; ls";
arg[0] will be ls, arg[1] will be date, and arg[2] will be ls after the above code.
I want to put each argument back into char string[100] and process it through algorithms.
one easiest way is to keep a backup of the original string in some temporary variable.
char string[100] = "ls ; date ; ls";
char temp_str[100] = {0};
strcpy (temp_str, string);
Another way is to do it by strcat. z has the number of agruments.
memset(string, '\0', 100);
for (i = 0; i < z; i++)
{
strcat(string, args[i]);
if (i != (z - 1))
{
//if it is last string dont append semicolon
strcat(string, ";");
}
}
Note : Take care of the boundary condition check
If you want the parts of string copied into a fixed length string[100] then you need to malloc 100 chars for each args[] inside the loop and strncpy() the result of strtok into it. strdup will only allocate enough memory for the actual length of the supplied string (plus \0)
This:
char** args = (char**)malloc(10*sizeof(char*));
memset(args, 0, sizeof(char*)*10);
is broken code. First, you shouldn't cast malloc()'s return value. Second, args is a pointer to ten pointers to char. You can't set them to NULL using memset(), there's no guarantee that "all bytes zero" is the same as NULL. You need to use a loop.

Building a basic shell, more specifically using execvp()

In my program I am taking user input and parsing it into a 2d char array. The array is declared as:
char parsedText[10][255] = {{""},{""},{""},{""},{""},
{""},{""},{""},{""},{""}};
and I am using fgets to grab the user input and parsing it with sscanf. This all works as I think it should.
After this I want to pass parsedText into execvp, parsedText[0] should contain the path and if any arguments are supplied then they should be in parsedText[1] thru parsedText[10].
What is wrong with execvp(parsedText[0], parsedText[1])?
One thing probably worth mentioning is that if I only supply a command such as "ls" without any arguments it appears to work just fine.
Here is my code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "308shell.h"
int main( int argc, char *argv[] )
{
char prompt[40] = "308sh";
char text[40] = "";
char parsedText[10][40] = {{""},{""},{""},{""},{""},
{""},{""},{""},{""},{""}};
// Check for arguments to change the prompt.
if(argc >= 3){
if(!(strcmp(argv[1], "-p"))){
strcpy(prompt, argv[2]);
}
}
strcat(prompt, "> ");
while(1){
// Display the prompt.
fputs(prompt, stdout);
fflush(stdout);
// Grab user input and parse it into parsedText.
mygetline(text, sizeof text);
parseInput(text, parsedText);
// Check if the user wants to exit.
if(!(strcmp(parsedText[0], "exit"))){
break;
}
execvp(parsedText[0], parsedText[1]);
printf("%s\n%s\n", parsedText[0], parsedText[1]);
}
return 0;
}
char *mygetline(char *line, int size)
{
if ( fgets(line, size, stdin) )
{
char *newline = strchr(line, '\n'); /* check for trailing '\n' */
if ( newline )
{
*newline = '\0'; /* overwrite the '\n' with a terminating null */
}
}
return line;
}
char *parseInput(char *text, char parsedText[][40]){
char *ptr = text;
char field [ 40 ];
int n;
int count = 0;
while (*ptr != '\0') {
int items_read = sscanf(ptr, "%s%n", field, &n);
strcpy(parsedText[count++], field);
field[0]='\0';
if (items_read == 1)
ptr += n; /* advance the pointer by the number of characters read */
if ( *ptr != ' ' ) {
strcpy(parsedText[count], field);
break; /* didn't find an expected delimiter, done? */
}
++ptr; /* skip the delimiter */
}
}
execvp takes a pointer to a pointer (char **), not a pointer to an array. It's supposed to be a pointer to the first element of an array of char * pointers, terminated by a null pointer.
Edit: Here's one (not very good) way to make an array of pointers suitable for execvp:
char argbuf[10][256] = {{0}};
char *args[10] = { argbuf[0], argbuf[1], argbuf[2], /* ... */ };
Of course in the real world your arguments probably come from a command line string the user entered, and they probably have at least one character (e.g. a space) between them, so a much better approach would be to either modify the original string in-place, or make a duplicate of it and then modify the duplicate, adding null terminators after each argument and setting up args[i] to point to the right offset into the string.
You could instead do a lot of dynamic allocation (malloc) every step of the way, but then you have to write code to handle every possible point of failure. :-)

Resources