Execlp vs Execl - c

Is there any occasion in which is better to use execl instead of execlp?
I think that maybe when a program is in two different folders using execlp could lead to confusion but I don't know if it is the only case.
I ask because one could think that writing execlp("ls", ...) is easier than writing execl("/bin/ls", ...).

Security
Looking programs up via PATH is convenient, but it can also be insecure. If a directory in a user's PATH is world writable, it's possible to inject a malicious program into the PATH lookup. This would affect execlp but not execl.
For example, if you had a PATH like /foo/bar/bin:/home/you/bin:/usr/bin:/bin and /foo/bar/bin was world writable, someone with access to that machine could copy a malicious program to /foo/bar/bin/ls. Then executing ls would run /foo/bar/bin/ls rather than /bin/ls. They'd be able to execute commands as you and gain greater access.
For this reason, it's often a good idea to refer to specific executables in known locations. Or to hard wire a secure PATH in the executable.
Compatibility
While there is a common set of Unix commands and features specified by POSIX, many programs rely on extensions. If your program uses those extensions, grabbing the first one in the PATH might not be a good idea.
For example, here on OS X the installed utilities in /bin and /usr/bin are BSD-flavored. But I have GNU versions installed earlier in my PATH. A program designed to run on OS X would want to explicitly use, for example, /bin/ls or /usr/bin/tar to be sure they get a known version of those utilities.
$ /usr/bin/tar --version
bsdtar 2.8.3 - libarchive 2.8.3
$ tar --version
tar (GNU tar) 1.29

Both execl() and execlp() work fine and similarly if your executables are in different folders or in the same folder, but you need to set the $PATH if different folders.
execl() is needed for executing executables (like ls) from command line as you can't go with execlp() in that case. I added a snapshot below.
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage Msg: ./a.out userdefined_executable\n");
return;
}
//execl(argv[1], argv[1], NULL); // it works
execlp(argv[1], argv[1], NULL); // it doesn't work
return 0;
}
// Input will be like this, here "p1" is an user-defined executable.
// xyz#xyz:~/stack_overflow$ ./a.out p1

Related

Why is this C program doing nothing in Ubuntu?

My very simple C program just hangs and I don’t know why.
I am trying to make a simple executable to handle multiple monotonous actions for me every time I start a new programming session.
So I decided with something simple (below) yet every time I run it, the app just hangs, never returns. So I have to Ctrl-C out of it. I have added printf commands to see if it goes anywhere, but those never appear.
My build command returns no error messages:
gcc -o tail tail.c
Just curious what I am missing.
#include <stdio.h>
#include <unistd.h>
int main() {
chdir("\\var\\www");
return 0;
}
There are at least two problems with the source code:
It is unlikely that you have a sub-directory called \var\www in your current directory — Ubuntu uses / and not \ for path separators.
Even if there was a sub-directory with the right name, your program would change directory to it but that wouldn't affect the calling program.
You should check the return value from chdir() — at minimum:
if (chdir("/var/www") != 0)
{
perror("chdir");
exit(EXIT_FAILURE);
}
And, as Max pointed out, calling your program by the name of a well-known utility such as tail is likely to lead to confusion. Use a different name.
Incidentally, don't use test as a program name either. That, too, will lead to confusion as it is a shell built-in as well as an executable in either /bin or /usr/bin. There is also a program /bin/cd or /usr/bin/cd on your machine — it will check that it can change directory, but won't affect the current directory of your shell. You have to invoke it explicitly by the full pathname to get it to run at all because cd is another shell built-in.
Two things:
First, that's not what Linux paths look like
Second, check the return value from chdir()
ie
if (chdir("/var/www") != 0)
printf("failed to change directory");
Finally, the effect of chdir() lasts for the duration of the program. It will not change the current directory of your shell once this program finishes.
The other answers adequately cover the issues in your C code. However, the reason you are seeing it hang is because you chose the name tail for your program.
In Linux, tail is a command in /usr/bin in most setups, and if you just type tail at the command line, the shell searches the $PATH first, and runs this. Without any parameters, it waits for input on its stdin. You can end it by pressing control-d to mark the end of file.
You can bypass the $PATH lookup by typing ./tail instead.
$ tail
[system tail]
$ ./tail
[tail in your current directory]
It is a good idea to use ./ as a habit, but you can also avoid confusion by not naming your program the same as common commands. Another name to avoid is test which is a shell built-in for testing various aspects of files, but appears to do nothing as it reports results in its system return code.

How to pass run time arguments to a function in c through a shell script

I have a shell script which has to take arguments from the command line and pass it to a function in C. I tried to search but didn't find understandable solutions. Kindly help me out.
Should the arguments be passed via an option as a command in the shell script?
I have a main function like this:
int main(int argc, char *argv[])
{
if(argc>1)
{
if(!strcmp(argv[1], "ABC"))
{
}
else if(!strcmp(argv[1], "XYZ"))
{
}
}
}
How to pass the parameters ABC/XYZ from the command line through a shell script which in turn uses a makefile to compile the code?
You cannot meaningfully compare strings with == which is a pointer equality test. You could use strcmp as something like argc>1 && !strcmp(argv[1], "XYZ"). The arguments of main have certain properties, see here.
BTW, main's argc is at least 1. So your test argc==0 is never true. Generally argv[0] is the program name.
However, if you use GNU glibc (e.g. on Linux), it provides several ways for parsing program arguments.
There are conventions and habits regarding program arguments, and you'll better follow them. POSIX specifies getopt(3), but on GNU systems, getopt_long is even more handy.
Be also aware that globbing is done by the shell on Unix-like systems. See glob(7).
(On Windows, things are different, and the command line might be parsed by some startup routine à la crt0)
In practice, you'll better use some system functions for parsing program arguments. Some libraries provide a way for that, e.g. GTK has gtk_init_with_args. Otherwise, if you have it, use getopt_long ...
Look also, for inspiration, into the source code of some free software program. You'll find many of them on github or elsewhere.
How to pass the parameters ABC/XYZ from the command line through a shell script
If you compile your C++ program into an executable, e.g. /some/path/to/yourexecutable, you just have to run a command like
/some/path/to/yourexecutable ABC
and if the directory /some/path/to/ containing yourexecutable is in your PATH variable, you can simply run yourexecutable ABC. How to set that PATH variable (which you can query using echo $PATH in your Unix shell) is a different question (you could edit some shell startup file, perhaps your $HOME/.bashrc, with a source code editor such as GNU emacs, vim, gedit, etc...; you could run some export PATH=.... command with an appropriate, colon-separated, sequence of directories).
which in turn uses a makefile to compile the code?
Then you should look into that Makefile and you'll know what is the executable file.
You are using and coding on/for Linux, so you should read something about Linux programming (e.g. ALP or something newer; see also intro(2) & syscalls(2)...) and you need to understand more about operating systems (so read Operating Systems: Three Easy Pieces).
See following simple example:
$ cat foo.c
#include <stdio.h>
int main(int argc, char ** argv)
{
int i;
for (i = 0; i < argc; ++i) {
printf("[%d] %s\n", i, argv[i]);
}
return 0;
}
$ gcc foo.c
$ ./a.out foo bar
[0] ./a.out
[1] foo
[2] bar
$

run a C program at startup [Red Pitaya]

I have a C program that needs to run when I turn on my machine (Red Pitaya).
the beginning of the program presented here:
//my_test program
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "redpitaya/rp.h"
int main(int argc, char **argv){
int jj=1;
while(1) {
printf("Ready for experiment number %i\n",jj);
int i, D;
int32_t TrigDly;
and so on...
the program is executable with a run.sh file called uri_test.sh, that contains the following:
cat /opt/redpitaya/fpga/fpga_0.94.bit>/dev/xdevcfg
LD_LIBRARY_PATH=/opt/redpitaya/lib ./my_test
both files are located in a directory under /root. the program is working perfectly when run manually on PuTTY terminal-
/RedPitaya/Examples/C/Uri# ./my_test
or
/RedPitaya/Examples/C/Uri# ./uri_test.sh
I tried to follow the solution presented here :
https://askubuntu.com/questions/9853/how-can-i-make-rc-local-run-on-startup
without success.
any suggestions? Thank you.
There are several ways to have a program running at startup, and it depends upon your init subsystem (are you using systemd or a SysV-style init?).
BTW, a source program in C is not a script and you generally compile it (using gcc -Wall -Wextra -g) into some executable. In your case, you probably want to set up its rpath at build time (in particular to avoid the LD_LIBRARY_PATH madness), perhaps by passing something like -Wl,-rpath,/opt/redpitaya/lib to your linking gcc command.
Perhaps a crontab(5) entry with #reboot could be enough.
Whatever way you are starting your program at startup time, it generally is the case that its stdin, stdout, stderr streams are redirected (e.g. to /dev/null, see null(4)) or not available. So it is likely that your printf output go nowhere. You might redirect stdout in your script, and I would recommend using syslog(3) in your C program, and logger(1) in your shell script (then look also into some *.log file under /var/log/). BTW, its environment is not the same as in some interactive shell (see environ(7)...), so your program is probably failing very early (perhaps at dynamic linking time, see ld-linux.so(8), since LD_LIBRARY_PATH might not be set to what you want it to be...).
You should consider handing program arguments in your C program (perhaps with getopt_long(3)) and might perhaps have some option (e.g. --daemonize) which would call daemon(3).
You certainly should read Advanced Linux Programming or something similar.
I recommend to first be able to successfully build then run some "hello-world" like program at startup which uses syslog(3). Later on, you could improve that program to make it work with your Red Pitaya thing.

Merging two programs in linux while preserving functionality

In Linux, is it possible to merge two binary programs into a single executable while still allowing both programs to execute?
I have several program binaries without source code and I wish to append them with my small program to display additional data for the user. Here is a small example:
prog1.c displays time information:
#include <stdio.h>
#include <time.h>
int main(){
time_t t = time(NULL);
struct tm time_stamp = *localtime(&t);
printf("Date: %d-%d-%d (mm/dd/yyyy) \n",time_stamp.tm_mon+1,time_stamp.tm_mday, time_stamp.tm_year+1900);
printf("Time: %d:%d \n", time_stamp.tm_hour, time_stamp.tm_min);
return 0;
}
prog2.c displays author info:
#include <stdio.h>
void main(){
printf("INFO: Originally developed by Jake.");
}
I wish to append prog1 with my prog2 such that calling prog1 will execute the prog2 and display author info as well. Output would look like:
Date: 11-19-2015 (mm/dd/yyyy)
Time: 11:46
INFO: Originally developed by Jake.
The idea sounds similar to self-extracting archives but have not seen a working example. Simply appending prog2 to the end of prog1 using cat, dd etc. attaches the prog2 but will not execute it.
In Linux, is it possible to merge two binary programs into a single executable while still allowing both programs to execute?
Of course that is impossible in general, and such an impossibility is not specific to Linux. AFAIK all the major OSes also have it. Read about executables, object files, ELF, linkers and Levine's book Linkers & loaders.
If you have access to the source code of both prog1 and prog2 (and you apparently don't) you might transform each of them to become a shared library, then code a wrapper which would dynamically loads one of them, e.g. with dlopen(3)
You could also change the source code to remove any potential name conflict (hence avoid having the same name defined in both), rename prog1's main to prog1_main, rename prog2's main to prog2_main, and have a simple wrapper like
extern int prog1_main(int, char**);
extern int prog2_main(int, char**);
int main(int argc, char**argv) {
if (!strcmp(basename(argv[0]), "prog1")
return prog1_main(argc, argv);
else if (!strcmp(basename(argv[0]), "prog2")
return prog2_main(argc, argv);
else { fprintf(stderr, "bad program name %s\n", argv[0]);
exit(EXIT_FAILURE); }
}
IIRC, SunOS3 did such tricks in 1987... (at that time, shared libraries did not exist as we have them today)
(Such a trick might not always work for C++, because it can have static data with constructors to be called before main, etc...)
Without source code, you might embed the binary executable of both prog1 and prog2 as data of another mixprog.c, compare argv[0] like above, and extract either prog1 or prog2 in some directory (perhaps a temporary one), then execute it (perhaps with execveat(2) on that directory). There might be both technical and legal issues with such an approach.
Alternatively, if prog2 only shows some message (which is unlikely), you might extract it with strings(1)
BTW, if both prog1 and prog2 are from your Linux distribution, they are likely to be free software, and you should work on their source code. If one of them is proprietary, be sure that its license permits you (legally) to do what you imagine.
If prog1 doesn't need to execute anymore once prog2 is launched, then you can make prog1 invoke prog2 through one function of the execv() family.
Another question with same prespective>
Yes, you can run this together, while searching for same I came up with this solution.
gcc prog1 prog2 -o prog_combined
After the execution you will get the executable file "prog_combined" in the same folder . Keep the two files in the same folder before execution.
./prog_combined
It worked for me.
In both the programs you have mentioned main functions, you can call one function as sub function to other using the command I have mentioned.

LD_PRELOAD affects new child even after unsetenv("LD_PRELOAD")

my code is as follows: preload.c, with the following content:
#include <stdio.h>
#include <stdlib.h>
int __attribute__((constructor)) main_init(void)
{
printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD"));
FILE *fp = popen("ls", "r");
pclose(fp);
}
then in the shell (do the 2nd command with care!!):
gcc preload.c -shared -Wl,-soname,mylib -o mylib.so -fPIC
LD_PRELOAD=./mylib.so bash
!!! be carefull with the last command it will result with endless loop of forking "sh -c ls". Stop it after 2 seconds with ^C, (or better ^Z and then see ps).
More info
This problem relate to bash in some way; either as the command that the user run, or as the bash the popen execute.
additional Key factors: 1) perform the popen from the pre-loaded library, 2) probably need to do the popen in the initialization section of the library.
if you use:
LD_DEBUG=all LD_DEBUG_OUTPUT=/tmp/ld-debug LD_PRELOAD=./mylib.so bash
instead of the last command, you will get many ld-debug files, named /tmp/ld-debug.*. One for each forked process. IN ALL THESE FILES you'll see that symbols are first searched in mylib.so even though LD_PRELOAD was removed from the environment.
edit: so the problem/question actually was: howcome can't you unset LD_PRELOAD reliably using a preloaded main_init() from within bash.
The reason is that execve, which is called after you popen, takes the environment from (probably)
extern char **environ;
which is some global state variable that points to your environment. unsetenv() normally modifies your environment and will therefore have an effect on the contents of **environ.
If bash tries to do something special with the environment (well... would it? being a shell?) then you may be in trouble.
Appearantly, bash overloads unsetenv() even before main_init(). Changing the example code to:
extern char**environ;
int __attribute__((constructor)) main_init(void)
{
int i;
printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD"));
printf("LD_PRELOAD: \"%s\"\n",getenv("LD_PRELOAD"));
printf("Environ: %lx\n",environ);
printf("unsetenv: %lx\n",unsetenv);
for (i=0;environ[i];i++ ) printf("env: %s\n",environ[i]);
fflush(stdout);
FILE *fp = popen("ls", "r");
pclose(fp);
}
shows the problem. In normal runs (running cat, ls, etc) I get this version of unsetenv:
unsetenv: 7f4c78fd5290
unsetenv: 7f1127317290
unsetenv: 7f1ab63a2290
however, running bash or sh:
unsetenv: 46d170
So, there you have it. bash has got you fooled ;-)
So just modify the environment in place using your own unsetenv, acting on **environ:
for (i=0;environ[i];i++ )
{
if ( strstr(environ[i],"LD_PRELOAD=") )
{
printf("hacking out LD_PRELOAD from environ[%d]\n",i);
environ[i][0] = 'D';
}
}
which can be seen to work in the strace:
execve("/bin/sh", ["sh", "-c", "ls"], [... "DD_PRELOAD=mylib.so" ...]) = 0
Q.E.D.
(The answer is a pure speculation, and may be is incorrect).
Perhaps, when you fork your process, the context of the loaded libraries persists. So, mylib.so was loaded when you invoked the main program via LD_PRELOAD. When you unset the variable and forked, it wasn't loaded again; however it already has been loaded by the parent process. Maybe, you should explicitly unload it after forking.
You may also try to "demote" symbols in mylib.so. To do this, reopen it via dlopen with flags that place it to the end of the symbol resolution queue:
dlopen("mylib.so", RTLD_NOLOAD | RTLD_LOCAL);
the answer from mvds is incorrect!
popen() will spawn child process which inherit the preloaded .so lied in parent process. this child process don't care LD_PRELOAD environment.

Resources