printf(*arg);
execvp(*arg, arg);
Here printf() statement prints value= ls.But when running program execvp gives there is no such file or directory.
else if (pid == 0) {
printf(*arg);
execvp(*arg, arg);
char* error = strerror(errno);
printf("shell: %s: %s\n", arg[0], error);
return 0;
if(execvp(arg[0], arg)<0)
{
printf("***ERROR: execution failedn\n");
}
return 0;
}
In the following code are two examples of how to use execvp.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *args[] = {"ls", "-l", NULL};
/* an example with a decleared array containing the commande */
if (fork() == 0) {
execvp(args[0], args);
}
/* another example where the commande was passed to main */
printf("argv[0] is the programme/executable name!\nargv[0] = %s\n", argv[0]);
if (fork() == 0) {
execvp(argv[1], argv + 1);
}
return EXIT_SUCCESS;
}
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.
Related
I have to program a little shell for school but I am stuck at even executing a command.
execvp worked when I executed it in the wait for input function, but in the execute command function it doesnt e.g I don't get any output to stdout for commands like ls.
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
int executecommand(char commandarray[]){
char * command = &commandarray[0];
pid_t pid = fork();
if (pid < 0){
perror("failed");
return errno;
} else if (0 == pid) {
execvp(command, &commandarray);
return 0;
} else {
int waitstatus;
waitpid(pid, &waitstatus, 0);
return 0;
}
}
int waitforinput(){
printf("$ ");
char cmd[256];
fgets(cmd, 256, stdin);
executecommand(cmd);
return 0;
}
int main(int argc, char **argv) {
waitforinput();
return 0;
}
The argument to execvp() must be an array of strings ending with NULL, not a pointer to a single string. Normally you would parse the input line into an array containing the command followed by arguments, but if you don't care about arguments you can just create an array with 2 elements, the command followed by NULL.
You also need to remove the newline from the end of the input line returned by fgets(). See Removing trailing newline character from fgets() input for many ways to do this.
If you want to return errno, you need to save it before calling perror(), because that may change errno.
int executecommand(char *commandarray[]){
char * command = commandarray[0];
pid_t pid = fork();
if (pid < 0){
int saved_errno = errno;
perror("fork");
return saved_errno;
} else if (0 == pid) {
execvp(command, commandarray);
int saved_errno = errno;
perror("execvp");
return saved_errno;
} else {
int waitstatus;
waitpid(pid, &waitstatus, 0);
return 0;
}
}
int waitforinput(){
printf("$ ");
char cmd[256];
char *cmd_array[2];
fgets(cmd, 256, stdin);
cmd[strcspn(cmd, "\n")] = 0; // remove trailing newline
cmd_array[0] = cmd;
cmd_array[1] = NULL;
return executecommand(cmd_array);
}
Invalid arguments to execvp :
From the man page:
int execvp(const char *file, char *const argv[])
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.
The execvp function expects an array of strings terminated by a NULL pointer, not a pointer to one string.
Ignoring the return value of exec* and others:
The exec() functions only return if an error has occurred. The return
value is -1, and errno is set to indicate the error.
Add a check for that.
Likewise, waitpid() may also fail, check its return value.
You also declared waitforinput() and executecommand() to return an int, but ignore the values returned. Either make use of them, or change the return type to void.
Trailing newline:
fgets(cmd, 256, stdin)
fgets will retain the newline in the buffer. Here's a one-liner that I use to remove it¹:
cmd [strcspn (cmd, "\n\r")] = `\0`;
Aside: Change
int main (int argc, char **argv)
to
int main (void)
as you never make use of the arguments.
[1] — Here are some others: https://codereview.stackexchange.com/q/67608/265278
I would like to take an argument, that is given to me through the terminal and use it for zipping the files. For example, I have main.c file that is look something like that.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...
int main(int argc, char **argv)
{
if(argc > 2){
printf("Enough arguments\n");
// tisk argumentu
printf("%s\n\n", argv[1]);
// tisk argumentů
for (int i; i < argc; i++){
printf("%s\n", argv[i]);
}
}
else{
printf("Insufficient number of arguments\n");
}
return 0;
}
and I give it through the terminal these parameters (gzip text1.txt text2.txt) like this.
$ gcc main.c -o main
$ ./main gzip text1.txt text2.txt
My question is how to take the parameter gzip which is in argv[1] and apply this function to the text1.txt and text2.txt.
This is what I have now. Problem is, that execlp takes place only once and I wanted to make this possible for all arguments (text1.txt, text2.txt --> later for more than two) now I am able to make it possible only for one bcs I hard code it there (argv[2]) --> that is the text1.txt and I need to make it possible for n files. Is there someone who will be able to answer the question "For what I should replace the argv[2] to gzip all files instead only one?"
#include <sys/types.h>
// we use unistd (in windows is process.h) library that allows us to execute program (such as gzip, ping etc) inside our program
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...
int main(int argc, char* argv[])
{
if(argc > 2){
printf("Enough arguments\n");
// printing second argument
printf("Program we use to compress: %s\n\n", argv[1]);
// printing all arguments
for (int i = 0; i < argc; i++){
printf("%d.argument: %s\n", i, argv[i]);
}
int tmp;
for (int i = 0; i < argc + 1; i++){
if (i < 2){
tmp ++;
}
else{
fork();
// execl (we use execlp to avoid defining path to pragram, or execvp where v stants for vector to pass more parametrs as argument) has firt two argument defined as program we want to execute to executables that we've sort as arguments to the reminal
execlp(
argv[1],
argv[1],
argv[2],
NULL
);
// if the program lands here we've got a problem because something went wrong so we use library errno to define an error that occurs
int err = errno;
if (err == 2){
printf("File with that name not found or path to that file was wrong!\n");
break;
}
}
}
}
else{
printf("Insufficient number of arguments\n");
}
return 0;
}
po úpravě
#include <sys/types.h>
// we use unistd (in windows is process.h) library that allows us to execute program (such as gzip, ping etc) inside our program
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...
int main(int argc, char* argv[])
{
if(argc > 2){
printf("Enough arguments\n");
// printing second argument
printf("Program we use to compress: %s\n\n", argv[1]);
// printing all arguments
for (int i = 0; i < argc; i++){
printf("%d.argument: %s\n", i, argv[i]);
}
for (int i = 2; i < argc + 1; i++){
// execl (we use execlp to avoid defining path to pragram, or execvp where v stants for vector to pass more parametrs as argument) has firt two argument defined as program we want to execute to executables that we've sort as arguments to the reminal
execlp(
argv[1],
argv[1],
argv[i],
NULL
);
// if the program lands here we've got a problem because something went wrong so we use library errno to define an error that occurs
int err = errno;
if (err == 2){
printf("File with that name not found or path to that file was wrong!\n");
break;
}
}
}
else{
printf("Insufficient number of arguments\n");
}
return 0;
}
The execv function takes an array of char *, the last of which must be NULL. The argv array fits this definition, so you can pass in the address of argv[1] which gives you the command specified and its parameters.
Also, you should check the return value of fork and only call execv in the child process.
if (fork() == 0) {
// child
execv(argv[1], &argv[1]);
perror("exec failed");
_exit(1);
} else {
// parent
wait(NULL);
}
There it is! Finall working solution
#include <sys/types.h>
// we use unistd (in windows is process.h) library that allows us to execute program (such as gzip, ping etc) inside our program
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...
int main(int argc, char* argv[])
{
if (argc > 2){
printf("Enough arguments\n");
// printing second argument
printf("Program we use to compress: %s\n\n", argv[1]);
// printing all arguments
printf("Arguments we use:\n");
for (int i = 0; i < argc; i++){
printf("%d.argument: %s\n", i, argv[i]);
}
// we start our loop from third argument
for (int i = 2; i < argc + 1; i++){
// splitting our program to n processes
int id = fork();
if (id == 0){
// execl (we use execlp to avoid defining path to pragram, or execvp where v stants for vector to pass more parametrs as argument) has firt two argument defined as program we want to execute to executables that we've sort as arguments to the reminal
execlp(
argv[1], // program we use
argv[1], // program we use
argv[i], // the document to which the program is applied
NULL
);
// if the program lands here we've got a problem because something went wrong so we use library errno to define an error that occurs
int err = errno;
if (err == 2){
printf("File with that name not found or path to that file was wrong!\n");
break;
}
}
}
}
else{
printf("Insufficient number of arguments\n");
}
return 0;
}
I'm working on a side project where I want to spawn 5 children processes then execvp into a second program called isEven which will tell if the number passed to it is even or not. However, the execvp call keeps failing as I ran it in gdb and it doesn't execute.
Args and load(args) are both called in another function.
Thanks in advance!!!
//Code to load binary
void load(char* args[])
{
execvp("./isEven", args);
printf("ERROR\n");
}
//args being passed in
char *args[] = {"./isEven", number, NULL};
load(args);
Here is a smaller program I wrote which does the same thing I'm trying to do right now. Gdb says I am getting a segmentation fault.
int main(int argc, char *argv[])
{
int returnCode = fork();
if(returnCode == 0)
{
char *args[] = {"./isEven", 12345, NULL};
execvp("./isEven", args);
printf(errno);
printf("ERROR: execvp failed\n");
exit(-1);
}
else if(returnCode < 0)
{
fprintf(stderr, "fork failed\n");
}
else
{
//i am the parent
printf("I am the parent");
}
return 1;
}
The main problem is with this line:
char *args[] = {"./isEven", 12345, NULL};
The args passed to execvp must be an array of char*. Having 12345 as an argument makes it interpret it as address and it's unlikely to be a valid address. In any case, that's not what you wanted to pass. So do:
char *args[] = {"./isEven", "12345", NULL};
You need to convert number into a string. For example:
int number = 12345;
char num_str[256];
snprintf(num_str, sizeof num_str, "%d", number);
char *args[] = {"./isEven", num_str, NULL};
execvp("./isEven", args);
perror("exec");
In your modified example, you are printing errno as:
printf(errno);
This will fail because printf expects a string as its first argument. You can use perror() to print the error as shown above.
I have an array of command strings I want to execute by calling execvp():
char* commands[] = ["ls -l", "ps -a", "ps"];
char* command = commands[0];
...
How do I execute the command with execvp?
Here's a possible usage example for you. This takes the command to execute from its arguments or you can uncomment the hardcoded example.
I recommend you look up the used commands in their respective man pages.
For execvp, the declaration is
int execvp(const char *file, char *const argv[]);
argv[0] should be the same as file by convention and argv should be NULL-terminated.
#include <stdlib.h> //exit
#include <stdio.h> //perror
#include <unistd.h>
#include <sysexits.h>
#include <errno.h>
#include <sys/wait.h>
int main(int argc, char** argv){
int pid, status, ret;
if((pid=fork())<0) { perror("fork"); exit(EX_OSERR); }
if(!pid){ //Child
/*
char* args[] = { "ps", "-a", (char*)0 };
execvp(args[0], args);
*/
//Execute arguments, already NULL terminated
execvp(argv[1], argv+1);
//exec doesn't exit; if it does, it's an error
perror(argv[1]);
//Convert exec failure to exit status, shell-style (optional)
switch(errno){
case EACCES: exit(126);
case ENOENT: exit(127);
default: exit(1);
}
}
//Wait on child
waitpid(pid, &status, 0);
//Return the same exit status as child did or convert a signal termination
//to status, shell-style (optional)
ret = WEXITSTATUS(status);
if (!WIFEXITED(status)) {
ret += 128;
ret = WSTOPSIG(status);
if (!WIFSTOPPED(status)) {
ret = WTERMSIG(status);
}
}
return ret;
}
I want to write a program which will create a new process and in that child process, it should execute the command: ls. In the meanwhile, the parent should wait for the child to die. However, my code does not work.
Please help me thank you very much!
int main()
{
char * read;
size_t size;
getline(&read , &size , stdin);
read[strlen(read)-1] = '\0';
printf("%s\n" , read);
int status;
pid_t f;
if((f = fork()) == 0)
{
execvp(read , &read);
exit(0);
}
else
{
wait(&status);
}
}
From man 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.
You need to use an array of char* and set the last element to NULL.
I am unsure what the getline() is reading but I guess it is the directory to be lsd. The first argument to execvp() should be ls and the second argument the array of char*.
Consider the following:
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
char *input_arg[2];
char *input_str = NULL;
size_t input_len = 0;
char **args;
ssize_t len;
size_t n;
pid_t child, p;
int status;
if (argc < 2) {
/* No command line parameters. Read command from stdin. */
len = getline(&input_str, &input_len, stdin);
/* Find length excluding the newline at end. */
if (len > (ssize_t)0)
n = strcspn(input_str, "\r\n");
else
n = 0;
if (n > (size_t)0) {
/* Terminate input command before the newline. */
input_str[n] = '\0';
} else {
fprintf(stderr, "No input, no command.\n");
return 1;
}
input_arg[0] = input_str;
input_arg[1] = NULL;
args = input_arg;
} else {
/* Use command line parameters */
argv[argc] = NULL;
args = argv + 1;
}
child = fork();
if (child == (pid_t)-1) {
fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
return 1;
}
if (!child) {
/* This is the child process. */
errno = ENOENT;
execvp(args[0], args);
fprintf(stderr, "%s: %s.\n", args[0], strerror(errno));
exit(127);
}
do {
p = waitpid(child, &status, 0);
} while (p == (pid_t)-1 && errno == EINTR);
if (p == (pid_t)-1) {
fprintf(stderr, "Lost child process: %s.\n", strerror(errno));
return 127;
}
if (p != child) {
fprintf(stderr, "waitpid() library bug occurred.\n");
return 127;
}
if (WIFEXITED(status)) {
if (!WEXITSTATUS(status))
fprintf(stderr, "Command successful.\n");
else
fprintf(stderr, "Command failed with exit status %d.\n", WEXITSTATUS(status));
return WEXITSTATUS(status);
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "Command died by signal %s.\n", strsignal(WTERMSIG(status)));
return 126;
}
fprintf(stderr, "Command died from unknown causes.\n");
return 125;
}
The above uses the command line parameters if specified, otherwise it reads one from the standard input. Because the standard input is not tokenized, you can only supply the command name, no parameters. If you enlarge the input_arg[] array into
char *input_arg[4];
and modify the assignment into
input_arg[0] = "/bin/sh";
input_arg[1] = "-c";
input_arg[2] = input_str;
input_arg[3] = NULL;
args = input_arg;
then the input string will be processed using the /bin/sh shell, just like popen() does.
You can also use len = getdelim(&input_str, &input_len, '\0', stdin); and remove the input_str[n] = '\0'; assignment to allow multiline input; the shell should handle those fine, as long as it is short enough to fit in the command line argument buffer (maximum length depends on your OS).
The rules how shells split input into separate commands and parameters are rather complex, and you should not try to emulate them. Instead, find a simple way for the user to specify the parameters separately (like the command-line parameter case), or use the shell to do it for you. If you don't do any splitting, you will probably need to remove the newline at the end of the input line.
The point to note is that for execvp(file, args), args[0] is the name the application sees (as $0 or argv[0]), and args[1] is the first parameter. Each parameter is terminated by NUL (\0) just like strings are normally in C, and the args pointer array must end with a NULL pointer. If there are no parameters, then args[1] == NULL.
why dont you just use system command...
#include <stdio.h>
#include <stdlib.h>
int main ()
{
int i;
printf ("Executing command ls...\n");
i=system ("ls");
printf ("The value returned was: %d.\n",i);
return 0;
}
Update:
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void main(void)
{
pid_t pid;
pid = fork();
if (pid == 0) // this is child process
{
int i;
printf ("Executing command ls...\n");
i=system ("ls");
printf ("The value returned was: %d.\n",i);
}
else // this is paraent process
{
int status=0
wait(&status);
printf ("Child process is returned with: %d.\n",status);
}
}