How to pass command line arguments to C program using execlp - c

I am trying to use execlp in a c program to run another c program. The exec function does call the program, but it does not pass the integer arguments correctly. My exec call is:
int exec_arg_1, exec_arg_2;
if(pid == 0){
printf("Repeat Number: %d, Process Number: %d\n", exec_arg_1, exec_arg_2);
execlp( "/home/drlight/Desktop/asp/Assignment_3/philosopher.o",
"philosopher.o", &exec_arg_1, &exec_arg_2, NULL );
printf("Exec didn't work...\n");
}
I assign values to the exec_arg ints, and print them right before to make sure they're correct, but the philosopher.o function just reads 0's from the location. If I run philosopher.o from the command line, it reads the arguments normally.

Arguments to programs are always strings.
int exec_arg_1, exec_arg_2;
if (pid == 0){
printf("Repeat Number: %d, Process Number: %d\n", exec_arg_1, exec_arg_2);
char arg1[20], arg2[20];
snprintf(arg1, sizeof(arg1), "%d", exec_arg_1);
snprintf(arg2, sizeof(arg2), "%d", exec_arg_2);
execlp( "/home/drlight/Desktop/asp/Assignment_3/philosopher.o",
"philosopher.o", arg_1, arg_2, NULL );
fprintf(stderr, "Exec didn't work...\n");
exit(1);
}
Note that execlp() is really only useful with a fixed number of arguments (or, a least, when there is a small fixed upper bound on the number of arguments). Most often, execvp() is a better choice.

This page includes plenty of usage examples....
EDIT : Added code snippet from the link
A code snippet from the link above
static void show_info_page(const char *git_cmd)
{
const char *page = cmd_to_page(git_cmd);
setenv("INFOPATH", system_path(GIT_INFO_PATH), 1);
execlp("info", "info", "gitman", page, (char *)NULL);
die(_("no info viewer handled the request"));
}
The best practice would be having a look at the execlp(3) man page in the first place I reckon.
EDIT : Added explanation of execlp(3) fro mthe man page
FreeBSD man page explains the usage of execlp() as follows
int
execlp(const char *file, const char *arg, ... /*, (char *)0 */);
The initial argument for these functions is the pathname of a file which
is to be executed.
The const char *arg and subsequent ellipses in the execl(), execlp(), and
execle() functions can be thought of as arg0, arg1, ..., argn. Together
they describe a list of one or more pointers to null-terminated strings
that represent the argument list available to the executed program. The
first argument, by convention, should point to the file name associated
with the file being executed. The list of arguments must be terminated
by a NULL pointer.
The functions execlp(), execvp(), and execvP() will duplicate the actions
of the shell in searching for an executable file if the specified file
name does not contain a slash ``/'' character. For execlp() and
execvp(), search path is the path specified in the environment by
``PATH'' variable. If this variable is not specified, the default path
is set according to the _PATH_DEFPATH definition in <paths.h>, which is
set to ``/usr/bin:/bin''
PS : some information, such as default search path, mat vary based on your system

Your issue is that execlp takes string pointers not integers for its arg parameters. From the manpage
int execlp(const char *file, const char *arg, ...);
You will have to convert these to strings before passing them to execlp.
#include<stdio.h>
#include<unistd.h>
#define MAXDIGITS 22
main()
{
int exec_arg_1, exec_arg_2;
char execStr1[MAXDIGITS + 1];
char execStr2[MAXDIGITS + 1];
exec_arg_1 = 750;
exec_arg_2 = 25;
snprintf(execStr1, MAXDIGITS + 1, "%d", exec_arg_1);
snprintf(execStr2, MAXDIGITS + 1, "%d", exec_arg_2);
printf("Our Strings: %s, %s\n", execStr1, execStr2);
execlp("/home/drlight/Desktop/asp/Assignment_3/philosopher.o", "philosopher.o", execStr1, execStr2, NULL);
}
You need to make sure MAXDIGITS is large enough to hold all the decimal digits of your number, but 25 should be sufficient for even longs on most current platforms. However keep in mind that in future versions of gcc and/or with different compilers this may be different. Don't forget to leave room for the negative either. You can check these maximums by importing limits.h and printing the values of INT_MAX and LONG_MAX.
#include<stdio.h>
#include<limits.h>
main(int argc, char * argv[])
{
printf("Int max: %d\n", INT_MAX);
printf("Long max: %ld\n", LONG_MAX);
}

Related

How to concat int or pid to string in C the way I like it doing

I have these lines that break and throws error at 439 memcpy(&path[0]+strlen("ls /proc/"),pid,1);
This is the code
void get_pid()
{
char line[1000];
FILE *cmd = popen("pidof a.out", "r");
fgets(line, 100, cmd);
pid_t pid = strtoul(line, NULL, 10);
char *path=malloc(sizeof(char)*100);
memset(path,0,100);
memcpy(path,"ls /proc/",strlen("ls /proc/"));
memcpy(&path[0]+strlen("ls /proc/"),pid,1);
system(path);
free(path);
return;
}
is there any neat way of accomplish I just to execute ls command the way I am trying it just takes /proc/ and concat with pid I got from system(pidof) Actually I need to get into ls /proc/my_pid=pid/fd/ and find socket fd. there is only one socket file in /proc/pid/fd/ directory
How to do it in C
Actually this line is trouble maker
memcpy(&path[0]+strlen("ls /proc/"),pid,1);
I am just taking the address of first character plusing it with strlen and then write with mcmcpy just 1. But obviously the above is wrong since my second parameter to memcpy is type int which is 4 byte and it expect char pointer which is 8 bytes. Can I cast it may be but I need to do it the correct way in C.
It is generally recommended to thing of what you are trying to do and what type are the data.
You first use popen to extract the result of the command pidof a.out. fgets will give you a string ending with a newline. Just replace that last character with a null
...
fgets(line, 100, cmd);
line[strlen(line) -1] = '\0';
You later want to concatenate that value to another string... It is useless to convert it to a pid_t only to convert it back to a string (unless you want to control that the format it correct).
Furthermore, the memxxx functions are recommended to process arbitrary character array that could contain null characters. Here you only handle null terminated strings, so you should use the strxxx functions.
Finally unless you want to experiment with it, using dynamic allocation for a fixed size array is non idiomatic... What you want is:
char path[100] = "ls /proc/";
That is enough to get an array of 100 characters initialized with the content of "ls /proc" followed by null characters.
Just concatenate your initial string:
strcat(path, line);
system(path);
Anyway, this is still poor code, because you should never rely on input data and should always test that what you get is what you expected, but I'll keep that for another answer...
There are a number of ways to go about finding the pid of the current process and then getting a directory listing of the process directory -- but you are making it harder than need be.
Most compiles provide the POSIX function getpid() which will retrieve the current process pid. To determine how many characters are needed for the combined "ls /proc/" and pid, just use the old snprinf() trick with the buffer NULL and the number of chars set to 0 and snprintf() will return the length that would be required to hold the completed string, e.g.
len = snprintf (NULL, 0, "ls /proc/%d", pid); /* determine len req'd */
With the length, you can allocate len + 1 bytes and then use sprintf() to fill the string, e.g.
if (!(path = malloc (len + 1))) { /* allocate len + 1 */
perror ("malloc-path");
return;
}
sprintf (path, "ls /proc/%d", pid); /* create path */
All that remains is making your system() call. Putting it altogether, you can do:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void get_pid ()
{
char *path;
int len;
pid_t pid = getpid(); /* get pid of current proc */
len = snprintf (NULL, 0, "ls /proc/%d", pid); /* determine len req'd */
if (!(path = malloc (len + 1))) { /* allocate len + 1 */
perror ("malloc-path");
return;
}
sprintf (path, "ls /proc/%d", pid); /* create path */
system (path); /* list contents of process directory */
free (path); /* free path */
}
int main () {
get_pid();
}
(note: for your use you can avoid the length checking and dynamic allocations simply be declaring path as a fixed-size array, e.g. char path[128]; would more than suffice, then you can simply call sprintf() and create the desired string)
The version using a fixed-size path simplifies to:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void get_pid ()
{
char path[128]; /* fixed buffer to hold final string */
pid_t pid = getpid(); /* get pid of current proc */
sprintf (path, "ls /proc/%d", pid); /* create path */
system (path); /* list contents of process directory */
}
int main () {
get_pid();
}
Either way is fine.
Example Use/Output
Running the code does provide a listing of the current process directory, e.g.
$ ./bin/lsprocpid
attr coredump_filter fdinfo make-it-fail mountstats oom_score_adj sched stat timerslack_ns
auxv cpuset gid_map map_files net pagemap schedstat statm uid_map
cgroup cwd io maps ns patch_state sessionid status wchan
clear_refs environ latency mem numa_maps personality setgroups syscall
cmdline exe limits mountinfo oom_adj projid_map smaps task
comm fd loginuid mounts oom_score root stack timers
This is probably one of the more concise ways to approach it. There are many ways with strcat(), etc, but when you need to mix numbers and text in a printf() like format-string, then using sprintf() to create the combined string is about as simple way as you have.
Look things over and let me know if you have further questions.

Why do we pass the command name twice to execve, as a path and in the argument list?

I have a program written by my professor that prints the working directory (pwd) by using execve(), but I don't understand the parameters.
pid_t pid = fork();
if(pid <0)
perror(NULL);
else if(pid == 0)
{
char*argv[] = {"pwd",NULL};
execve("/bin/pwd",argv,NULL);
perror(NULL);
}
else
printf("Im the parent!");
return 0;
}
"/bin/pwd" gives the path to the executable that will be executed.
This means that it will call the pwd function, doesn't it?
Then why do I need to have the parameter pwd?
Couldn't the program run without that parameter?
By convention, the first argument passed to a program is the file name of the executable. However, it doesn't necessarily have to be.
As an example, take the following program:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
printf("number of arguments: %d\n", argc);
printf("program name: %s\n", argv[0]);
for (i=1; i<argc; i++) {
printf("arg %d: %s\n", argv[i]);
}
return 0;
}
If you run this program from another like this:
char*argv[] = {"myprog", "A", "B", NULL};
execve("/home/dbush/myprog",argv,NULL);
The above will output:
number of arguments: 3
program name: myprog
arg 1: A
arg 2: B
But you could also run it like this
char*argv[] = {"myotherprog", "A", "B", NULL};
execve("/home/dbush/myprog",argv,NULL);
And it will output:
number of arguments: 3
program name: myotherprog
arg 1: A
arg 2: B
You can use the value of argv[0] as a way to know how your program was called and perhaps expose different functionality based on that.
The popular busybox tool does just this. A single executable is linked with different file names. Depending on which link a user used to run the executable, it can read argv[0] to know whether it was called as ls, ps, pwd, etc.
The execve man page has some mention of this. The emphasis is mine.
By convention, the first of these strings should contain the filename associated with the file being executed.
That is, it is not a actually mandatory for the first argv to be the filename. In fact one can test that by changing the argv[0] to any string in the example code and the result will still be correct.
So it really is just a convention. Many programs will use argv[0] and expect it to be the filename. But many programs also do not care about argv[0] (like pwd). So whether argv[0] actually needs to be set to the filename depends on what program is being executed. Having said that, it would be wise to always follow the convention to play nicely with almost everyone's long held expectations.
From execve man page: http://man7.org/linux/man-pages/man2/execve.2.html
argv is an array of argument strings passed to the new program. By
convention, the first of these strings (i.e., argv[0]) should contain
the filename associated with the file being executed. envp is an
array of strings, conventionally of the form key=value, which are
passed as environment to the new program. The argv and envp arrays
must each include a null pointer at the end of the array.
So, argv is treated as command line args for new program to execute with.
Since by default, for a linux binary invoked with arguments, these args are accessed through argc/argv, where argv[0] holds the program name.
I think this is to keep the behavior parity to match with default case (prog invoked with arguments).
From the source:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/exec.c#l1376
The argv passed to execve is used to construct argv for the about to be launched binary.

printing agruments using text interpreter and execl

I am trying to print command line arguments using execl function which executes a text interpreter but I am not able to get the first argument printed.
this is my main program
#include<unistd.h>
#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char *argv[])
{
pid_t pid;
if((pid=fork())<0)
printf("error\n");
else if(pid==0)
if((execl("textinterpreter","this","is","usp","lab",(char*)0))<0)
perror("error in execl\n");
if(waitpid(pid,NULL,0) !=pid)
printf("error1\n");
system("ls > list");
return 0;
}
This is my textinterpreter file
#!/home/chirag/echoarg1 my1
This is my echoarg1.c file
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
main(int argc,char *argv[])
{
int i;
for(i=0;i<argc;i++)
printf("argv[%d]=%s\n",i,argv[i]);
}
The output I am getting is
argv[0]=/home/chirag/echoarg1
argv[1]=my1
argv[2]=textinterpreter
argv[3]=is
argv[4]=usp
argv[5]=lab
Where as the expected output is
argv[0]=/home/chirag/echoarg1
argv[1]=my1
argv[2]=textinterpreter
argv[3]=this
argv[4]=is
argv[5]=usp
argv[6]=lab
Can someone please point out the mistake.
from the execl man page:
int
execl(const char *path, const char arg0, ... /, (char *)0 */);
and
The initial argument for these functions is the pathname of a file which
is to be executed.
The const char *arg0 and subsequent ellipses in the execl(), execlp(),
and execle() functions can be thought of as arg0, arg1, ..., argn.
Together they describe a list of one or more pointers to null-terminated
strings that represent the argument list available to the executed program. The first argument, by convention, should point to the file name
associated with the file being executed. The list of arguments must be
terminated by a NULL pointer.
so in your line:
if((execl("textinterpreter","this","is","usp","lab",(char*)0))<0)
"textinterpreter" is the path of the file to execute, but execl is expecting the next argument, arg0, to "point to the file name associated with the file being executed".
in other words it should look more like:
if((execl("textinterpreter","textinterpreter","this","is","usp","lab",(char*)0))<0)
Usually the first two args to execl would likely be the same, but it allows you the flexibility to specify one arg for finding the file to execute and another argument to pass as argv[0].
As an experiment, try changing your execl to call echoarg1 directly like this:
if((execl("echoarg1","FAKEPATH","this","is","usp","lab",(char*)0))<0)
You should see output like:
argv[0]=FAKEPATH
argv[1]=this
argv[2]=is
argv[3]=usp
argv[4]=lab
In your example it appears that it's displaying "this" instead of "textinterpreter" as a result of the how the bash interpreter handles your "textinterpreter" script. The bash interpreter appears to acting as follows:
takes the hash-bang line and runs /home/chirag/echoarg1
pass it argv[0]=/home/chirag/echoarg1
pass it the rest of the hashbang line as additional args (in this case just argv[1]=my1)
as it appends the original argv list it appears use the executable path ("textinterpreter") instead of the actual argv[0] ("this") and then continues with argv[1] ("is") and the rest...
It is not the bash interpreter (whatever that's supposed to mean - there is no bash involved here) which discards arg0, but it is the kernel's function load_script() with the statement
retval = remove_arg_zero(bprm);
above it there is a comment saying
* Splice in (1) the interpreter's name for argv[0]
* (2) (optional) argument to interpreter
* (3) filename of shell script (replace argv[0])
- in your case, (1) /home/chirag/echoarg1, (2) my1, (3) textinterpreter (replace this).

How to run c program and give input in same line

I'm new to C and I'd like to ask about running a C program and supplying input at the same time.
What I would like to do is run a program (ex. fileOpener) and also state which file to open
./fileOpener < filename1
I've tried it already and it works fine, but what do I use to know what filename1 is? That way I can open the file with
fp = fopen(filename1, "r")
Thanks.
Edit: OK, I'll try to explain a bit more. If there wasn't a "<" then I could just use command line arguments as I have done before, but when I tried it with the <, it didn't work
Specifically: fileOpener code:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
printf("%s", argv[1]);
}
when I use ./fileOpener < filename1 the output is ./fileOpener
I used gcc -o fileOpener fileOpener.c as the compiler
int main(int argc, char *argv[])
You can name them whatever you want, but these are the normal names.
argc is non-negative. It gives the number of useful elements in argv.
If argc is positive, argv[0] contains the program name. Then argv[1] through argv[argc - 1] point to character arrays that contain the program's command line arguments.
For example, if I run a program at the command line, such as
unzip filename.zip
argc will equal 2; and argv[0] will compare equal to "unzip"; and argv[1] will compare equal to "filename.zip".
Source
You can't do that, if you use redirection (i.e. "< filename") the file is opened by the system. You could discover the name, but it's non-portable, and anyway useless since the file is already open. Just use stdin instead of fp, and you need not use fopen() (nor fclose()):
int main()
{
char buffer[1024];
// fgets() reads at most 1024 characters unless it hits a newline first
// STDIN has been already opened by the system, and assigned to data flowing
// in from our file ( < inputFile ).
fgets(buffer, 1024, stdin);
printf("The first line of input was: %s", buffer);
}
A different approach is to use arguments:
int main(int argc, char **argv)
{
FILE *fp = NULL;
char buffer[1024];
if (argc != 2)
{
fprintf(stderr, "You need to specify one argument, and only one\n");
fprintf(stderr, "Example: %s filename\n", argv[0]);
// Except that argv[0], this program's name, counts.
// So 1 argument in command line means argc = 2.
return -1;
}
printf("I am %s. You wanted to open %s\n", argv[0], argv[1]);
fp = fopen(argv[1], "r");
fgets(buffer, 1024, stdin);
printf("The first line of input was: %s", buffer);
fclose(fp); fp = NULL; // paranoid check
return 0;
}
You need setup your program to take a command line argument. Here's a good tutorial that solves your exact question:
http://www.cprogramming.com/tutorial/c/lesson14.html
A program's main function in C has two arguments:
int main(int nArgs, char *pszArgs[]) {}
That first argument tells the program how many parameters were passed onto the program when you ran it. Usually, this will just be 1, because it includes the program's name.
The second argument is a table of strings, which can be accessed thus (the program below prints the parameters given to it):
int main(int nArgs, char *pszArgs[])
{
int i = 0;
while (i < nArgs)
{
printf("param %d: %s\n", i, pszArgs[i]);
i++;
}
return 0;
}

C: Pass array in place of variable argument list

I am writing a simple shell in C. It is actually coming along quite well with i/o redirection and such. One thing that I'd like to add is a way to switch between versions of the exec* functions. Right now I'm sticking to execlp(), execvp(), and execve().
I have all the arguments I want to pass in an array called argv. It is a null terminated array of null terminated strings, so it works fine with execv*, but I can't think of how to get it to work with execlp().
This is what I have now:
if (strcmp(exec_opt, "vp") == 0)
error = execvp(argv[0], argv); /* Execute vp */
else if(strcmp(exec_opt, "lp") == 0)
error = execlp(argv[0], "", argv); /* Execute lp */
else if(strcmp(exec_opt, "ve") == 0)
error = execve(argv[0], argv, environ); /* Execute ve */
else
{
// throw errors about exec_opt
}
if(error != 0)
{
// do something about it
}
In this configuration the compiler doesn't baff at the syntax, but it also doesn't work.
I've also tried
error = execlp(argv[0], (char*) argv); /* As a single string */
char* argv1 = argv[1]; /* don't pass command itself */
error = execlp(argv[0], argv1);
Which do various odd but ultimately incorrect things. Is there a way for me to turn my array into a variable argument list? passing it directly (which makes the most type-sense, since variable argument lists are char* argv[]) yields a compiler error about casting incompatible pointers.
You can't really use execlp() with the array. To use execlp(), you have to write out:
execlp(array[0], array[0], (char *)0);
execlp(array[0], array[0], array[1], (char *)0);
execlp(array[0], array[0], array[1], array[2], (char *)0);
execlp(array[0], array[0], array[1], array[2], array[3], (char *)0);
...
for each alternative number of arguments. This is why execvp() was added to the repertoire (it wasn't part of 7th Edition UNIX™ in 1978, but was part of SUS v2 in 1997). Now you just need execvpe(), which does not exist AFAIK (and I don't know why it isn't provided, either).
7th Edition UNIX did have excevp()
Dave said:
The 7th Edition manual does list execvp.
And ... it does, partially. I think we have an erratum to report since the manual actually contains:
NAME
execl, execv, execle, execve, execlp, execvp, exec, exece, environ – execute a file
SYNOPSIS
execl(name, arg0, arg1, ..., argn, 0)
char *name, *arg0, *arg1, ..., *argn;
execv(name, argv)
char *name, *argv[ ];
execle(name, arg0, arg1, ..., argn, 0, envp)
char *name, *arg0, *arg1, ..., *argn, *envp[ ];
execve(name, argv, envp);
char *name, *argv[ ], *envp[ ];
extern char **environ;
DESCRIPTION
So, execvp() is listed in the NAME section, but there is no synopsis for execvp() (which is what I looked at to come to the conclusion it was missing). There is a reference to execvp() on the next page:
Execlp and execvp are called with the same arguments as execl and execv, but duplicate the shell’s actions in searching for an executable file in a list of directories. The directory list is obtained from the environment.
So, I excuse myself because I scanned the SYNOPSIS and execvp() was omitted from the synopsis. But actually, the system call was present in 7th Edition Unix. I don't think anyone is about to re-release the manuals with the omission fixed.
My print copy (yes, I do have printed properly bound versions of both Volume 1 (ISBN 0-03-061742-1) and Volume 2 (ISBN 0-03-061743-X) of the manual from way back then; I obtained them circa 1989) has the same omission in the SYNOPSIS section.
Try using avcall. I haven't used it myself, I just found an interesting mention of it here: Passing parameters dynamically to variadic functions

Resources