Running BASH command via C program - c

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;
}

Related

C, I want to pass 3 arguments to a bash script, I am a noob in c

I have read a little and learned a little about syestem function in c.
So, assuming I have a bash file ./some.sh that takes three arguments how should I make this code work? It will not compile with an error about the buffer.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
snprintf(buffer, sizeof(buffer), "/bin/bash ./some.sh %s %s %s", target1, target2, target3);
system(buffer)
}
Slightly more complicated, but safer, would be to avoid system and explicitly call fork and some version of exec (below, I use execl). (Error handling omitted for simplicity.) This avoids the need to ensure that each argument is correctly quoted for creating a shell command line.
int main(void) {
// ...
if (fork() == 0) {
execl("/bin/bash", "./some.sh", target1, target2, target3, (char *)0);
}
}
(Note: the approach is sound; my actual C implementation may leave something to be desired.)
You've used the buffer variable without actually declaring it. This works in Bash, but not in C. If you wanted buffer to be an array of characters, say, 1024 characters long, you could write:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char buffer[1024];
snprintf(buffer, sizeof(buffer), "/bin/bash ./some.sh %s %s %s", target1, target2, target3);
system(buffer)
}
(Of course, target1, target2 and target3 have to exist as well, but I assume those are placeholder names.)

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).

C: How can I exec() my program with the same command line arguments as my running program

I am trying to reset my program when it receives a SIGSEGV by using ececl() in my signal handler. But, my current program needs commandline arguments to start that I can pass via execl() + 1 extra argument "RESTART" to notify the program that it just restarted instead of a fresh start.
But how can I pass my argv[] via exec()?
Objective:
execl("./myprog","./myprog",argv[1],argv[2],...,argv[argc],"RESTART");
OR
execl("./myprog","./myprog","RESTART",argv[1],argv[2],...,argv[argc]);
Use execv():
SYNOPSIS
#include <unistd.h>
...
int execv(const char *path, 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.
Perhaps like this:
int main( int argc, char **argv )
{
...
int rc = execv( "./myprog", argv );
}
You may need to modify specific values in argv or create an entirely new argument array to fit what you need.
You need to save argv in a global, either from main:
static char **Argv;
int main(int c, char **v) { Argv = v; //...
or from a gcc constructor:
static char **Argv;
__attribute__((constructor))
static void ctor(int c, char **v) { Argv = v; }
Then you can do what you want:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
static char **Argv;
static void hndlr(int s)
{
execv("/proc/self/exe", Argv);
_exit(127);
}
int main(int argc, char **argv)
{
Argv = argv;
struct sigaction sa = { .sa_handler = hndlr, .sa_flags = SA_NODEFER };
sigaction(SIGSEGV, &sa, 0);
sleep(1);
fputs("start\n", stderr);
//keep re-executing the same program
raise(SIGSEGV);
}
Note that without the SA_NODEFER, you'll only see the message twice, because SIGSEGV will be blocked during the second run of the executable.
While this should be defined (especially if you add a signal stack so that you can handle stack overflows with this too), wrappers scripts/programs are a safer and more robust way of doing this. With the SISEGV handler approach, you aren't really starting from scratch -- you are inheriting signal masks, effective uids/gids, workings directories, open file descriptors, etc. etc., whereas with a wrapper script you start from a well defined state.

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

having some troubles with execv (bash 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

Resources