having some troubles with execv (bash c) - c

i want to write a program which executes the command passed in argument to a .c file, but i'm having incomprehensible errors coming from the declaration of an array of char pointers; my code is the following:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
if(argc < 2)
{
printf("Incorrect number of arguments.\n");
return -1;
}
char* args[1 + argc - 2]; // this is causing the problem
char cmd[10];
/*
* some operations...
*/
char* test[] = {"ps","-l","NULL"};
execv("/bin/ps",test);
return 0;
}
the arguments of execv should normally be cmd and args, but it didnt work, so i tried a simple command, didnt work either, i'm getting the following error:
error: unsupported option (BSD syntax)
Usage:
ps [options]
Try 'ps --help <simple|list|output|threads|misc|all>'
or 'ps --help <s|l|o|t|m|a>'
for additional help text.
For more details see ps(1).
however, if i put 1 as the size of the args array, the ps command is executed... i'm pretty confused, and i'd appreciate any kind of help

Related

Why execvp accepts 2 arguments

Assume the following code in c:
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char * argv[]) {
char *args[] = {"/bin/echo", "1", NULL};
execvp(args[0], args);
}
Why does execvp accept 2 arguments? Why can't it be execvp(args) and internally it would automatically grab the executable to run from args[0] ?
What if you want the "name" of the process (args[0] in your case) to be different from the actual executable program? That is a valid use-case, and the simplest solution to handle both the cases is the two-argument execvp we now have.

Running find from a C program to print file name given inode number?

I am trying to print the file name given the inode number. I am using the execlp system call to run the find command. My code is shown below:
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
char str[8];
char *ptr;
ptr=str;
long x=9306140;
snprintf(str,8,"%ld", x);
execlp("find"," ","~"," ","-inum"," ",str,NULL);
}
But I get an error: Invalid argument '9306140' to -inum.
Could someone please help?
As noted in comments, you don't want the " " (space) arguments in the execlp() argument list. The shell takes a string with spaces separating the arguments, but it treats what's between the spaces as words that are passed to the command; it doesn't ordinarily pass the spaces to the command. (That's a simple and sufficient explanation for this exercise; there are caveats and weasel-words galore that could be added.)
The shell also expands ~ to match the value in the $HOME environment variable (in contrast to ~user which gets the home directory of user from the password file — they're usually, but not necessarily, the same).
You also have little margin for error in the size of string you've allocated for the number. Frankly, though, you should simply use a string. All these changes lead to:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
const char *home = getenv("HOME");
const char inode[] = "9306140";
execlp("find", "find", home, "-inum", inode, (char *)NULL);
fprintf(stderr, "failed to execute 'find' command\n");
return(EXIT_FAILURE);
}
Note the repeated "find"; the first is the name of the file to be executed after searching along the path; the second is the value to be provided as argv[0]. You could put "hypochondriac" as the second occurrence and it is likely that find would work the same, at worst reporting its error messages as coming from the program 'hypochondriac'.
The next step would be to take the inode number from a command line argument:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s inode\n", argv[0]);
exit(EXIT_FAILURE);
}
const char *home = getenv("HOME");
const char *inode = argv[1];
execlp("find", "find", home, "-inum", inode, (char *)NULL);
fprintf(stderr, "%s: failed to execute 'find' command\n", argv[0]);
return(EXIT_FAILURE);
}
The step after that would be to handle multiple inode values; at that point, you use execvp() instead of execlp(), though (or, if you're desparate and lazy, you loop and fork() and execlp() once per inode number, but that's slamming your system for no good reason at all).

Running BASH command via C program

I'm trying to run BASH commands via a C program, but i'm struggling with the function execv. I don't really know how to write the first parameter of that function. I tried with the strcat function to append the string "/bin/" with the 1st element of the argv tab, which is the command i write when I run my program, but it just doesn't work. I get a "Segmentation fault". Instead of using the strcat function I tried with strdup, but I don't know how to use it right.
Any help would be appreciated. My program is below.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[], char *envp[]){
char *tab[] = {argv[1],NULL};
if(execve(strcat("/bin/",argv[1]), tab, envp)==-1)
{
perror("execve");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
regarding:
if(execve(strcat("/bin/",argv[1]), tab, envp)==-1)
This will not work ,
the literal "/bin/" is in read only memory, so cannot be changed (need a char buffer large enough to hold the full string, similar to `char string[100] = "/bin/";
Suggest:
#include <stdio.h> // perror()
#include <stdlib.h> // exit(), EXIT_FAILURE
#include <sys/types.h>
#include <sys/wait.h> // waitpid()
#include <unistd.h> // fork(), execvp()
#include <string.h> // strlen(), strcpy(), strcat()
int main( int argc, char *argv[], char *env[] )
{
(void)argc;
char *tab[] = { argv[1], NULL };
char string[strlen(argv[1]) + strlen( "/bin/" ) +1 ];
strcpy( string, "/bin/" );
strcat( string, argv[1] );
int status;
pid_t pid = fork();
switch( pid )
{
case -1: // fork failed
perror( "fork failed" );
exit( EXIT_FAILURE );
break;
case 0: // child process
execve( string, tab, env ); // does not return unless an error
perror("execve failed");
exit( EXIT_FAILURE );
break;
default:
waitpid( pid, &status, 0 );
break;
}
}
Caveat: the proposed code just hides the parameter: argc rather than properly checking it to assure the command line does contain a parameter.
Caveat: the parameter to main: env[] is not portable and should not be used. Suggest using :
extern char *environ[];
To run a shell command from a C program, you should use system(3).
If you want to get its stdout (or give its stdin, but not both) use popen(3) (don't forget to pclose such a stream).
The shell used (by system and popen) is not exactly bash but the standard POSIX /bin/sh (quite similar to bash with some restrictions).
To build that shell command (but beware of code injections in it) you can use common string functions such as snprintf and asprintf.
Notice that execve(2) does not return when it is successful, and it does not run a command thru a shell, but directly an executable program. Actually Unix shells (such as bash or  /bin/sh) are using fork(2), execve(2), waitpid(2) very often and are implementing globbing. BTW system & popen are also using fork and execve on /bin/sh -c.
strcat("/bin/",argv[1])
is horribly wrong, the first argument to strcat is the overwritten destination buffer (so cannot be a string literal), and you don't check against buffer overflow.
You might want to code:
char progbuf[80];
int ln = snprintf(progbuf, sizeof(progbuf), "/bin/%s", argv[1]);
and you should check later that ln<(int)sizeof(progbuf)
BTW, your program, when you'll improve it, is not using Bash; it is directly executing some command.
I tried with strdup, but I don't know how to use it right.
Before using any function, you need to carefully read its documentation, for example strdup(3) (or type man strdup in a terminal).
Melpomene is right- you can't use strcat like that. In C, you can't return strings. What you do is pass a memory address (pointer) as the first argument in strcat, and then strcat modifies the memory pointed to by it so that it also contains the second argument of strcat. This is a process you will repeat over and over again in C, so you should understand it. Also, that strcat doesn't look safe, I bet there is a strcatn function or something like that.
I finally got to find a way to do what I wanted at the first place. Thanks to all of you guys for your help & advices ! Here's the solution !
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[], char *envp[]){
char *tab[] = {argv[1],argv[2],NULL};
char test[20] = "/bin/";
if(execve(strcat(test,argv[1]), tab, envp)==-1)
{
perror("execve");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

How to reproduce "2 No such file or directory" in C

I know it might sound strange but I'm trying to find when to get this type of error when passing a wrong argument to a main program.
Let's say I have a program that accepts 1 or 2 arguments. If it's 2 arguments it can only be:
argv[0] =./programName
and
argv[1] = "-A".
Any other argv[1] other than "-A" needs to printf a "2 No such file or directory" message.
As far as I know, this is a system message, so printing it will not work for me.
DO I need to save all possible main arguments in a file and then compare the typed arguments with the ones in the file?
Right now the way I have it is:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/times.h>
#include <sys/wait.h>
int main (int argc, char *argv[]){
.....
...
if (argc == 2 && strcmp(argv[1], ARGV_2)!=0){
return(EXIT_FAILURE);
}
...
.....
}
I think ls does this with the error() function:
GNU Error_messages
Function: void error (int status, int errnum, const char *format, …)
Preliminary: | MT-Safe locale | AS-Unsafe corrupt heap i18n | AC-Safe
| See POSIX Safety Concepts.
The error function can be used to report general problems during
program execution. The format argument is a format string just like
those given to the printf family of functions. The arguments required
for the format can follow the format parameter. Just like perror,
error also can report an error code in textual form. But unlike perror
the error value is explicitly passed to the function in the errnum
parameter. This eliminates the problem mentioned above that the error
reporting function must be called immediately after the function
causing the error since otherwise errno might have a different value.
error prints first the program name. If the application defined a
global variable error_print_progname and points it to a function this
function will be called to print the program name. Otherwise the
string from the global variable program_name is used. The program name
is followed by a colon and a space which in turn is followed by the
output produced by the format string. If the errnum parameter is
non-zero the format string output is followed by a colon and a space,
followed by the error message for the error code errnum. In any case
is the output terminated with a newline.
The output is directed to the stderr stream. If the stderr wasn’t
oriented before the call it will be narrow-oriented afterwards.
The function will return unless the status parameter has a non-zero
value. In this case the function will call exit with the status value
for its parameter and therefore never return. If error returns, the
global variable error_message_count is incremented by one to keep
track of the number of errors reported.
So maybe something like this would achieve OP's goal as well as the other answers suggested before:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <error.h>
int main(int argc, char* argv[])
{
if (argc == 2 && strcmp(argv[1], "-A") != 0) {
error(ENOENT, ENOENT, "cannot access %s", argv[1]);
}
printf("program didn't get to here\n");
}
Outputs of ls and this example:
~/workspace/tests/ $ ./ctest bogus_dir
./ctest: cannot access bogus_dir: No such file or directory
~/workspace/tests/ $ ls bogus_dir
ls: cannot access bogus_dir: No such file or directory

How to fix Segmentation fault in C

Hello i wrote my c program which will be run on linux.
I am trying to make my own shell for linux.
I have the following code below...
#include <limits.h>
#include <libgen.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAX_LINE 80 /* 80 chars per line, per command, should be enough. */
int main(void){
int i = 0;
int k = 0;
int argsCount = 0;
char inputBuffer[MAX_LINE]; /*buffer to hold command entered */
int background; /* equals 1 if a command is followed by '&' */
char *args[MAX_LINE/2 + 1]; /*command line arguments */
pid_t tpid ;
pid_t child_pid;
int child_status;
char path[PATH_MAX+1];
char *progpath = strdup(args[0]);
char *prog = basename(progpath);
char temp[MAX_LINE];
}
It'is compiling well but when i try to run the code it gives me segmentation fault error
How can i fix it and why i take this error?
Your main has a wrong signature. You want
int main(int argsCount, char**args) {
and of course you should remove the internal declaration of argCount & args inside your main.
Perhaps you want instead your args & argCount to contain the parsed arguments of your own shell (but you still have to give a good signature to your main, conventionally and very often int main(int argc, char**argv).... you probably want your shell to accept the -c argument as most shells do, this would ease debugging with simplistic test cases). Then you should initialize them, and you should read some line (probably with getline) in a loop.
As I commented, you should compile with all warnings & debug info:
gcc -Wall -Wextra -g yoursource.c -o yourprog
Then use gdb ./yourprog to debug your program (see GDB documentation). valgrind should also be helpful. Of course, be sure to develop on a Linux system!
BTW, your program is not a convincing start for a shell. Use strace on some existing shell to understand what a shell needs to do. Study the source code of some existing free software shell (e.g. sash, fish, GNU bash ...). Read Advanced Linux Programming

Resources