I am writing a C program which determines the number of bytes read from the standard input . I
found out there are ways to give input to the program
piped input
redirection
entering into command line while the program is waiting for input
How to find the exact command by which the program was executed from the shell .
I tried using command-line arguments but failed .
#include <stdio.h>
int main(int argc,char *argv[])
{
char buffer[100];
int n;
for(n=1;n<argc;n++)
printf("argument: %s\t",argv[n]);
printf("\n");
if(argc==1)
printf("waiting for input :");
else if (argc==3)
printf("Not waiting for input . Got the source from command itself .");
n = read(0,buffer,100);
if(n==-1)
printf("\nError occured in reading");
printf("\nReading successfully done\n");
return 0;
}
Also ,
In general, you can't do that from inside your program - the shell might not pass along some of those arguments to you. It will have expanded globs, done I/O redirection and so forth, all before your program ever runs or gets arguments.
You can try calling out to ps -o args, which might work out for you. It won't give redirections as far as I know, though.
you have some options, check argv to see how it was invoked (argv[0] to tell whether it was invoked as full path, relative path, current directory or using $PATH based on preceding /s .s or lack thereof )
you can get the parent process that invoked it with something like:
sprintf(buf,"/proc/%d/cmdline",getppid());
fd=open(buf,O_RDONLY);
read(fd,buf,buf_size);
write(1,buf,strlen(buf));
you can also get other info from /proc/pid/... for the current command using getpid as above (not getppid)
Once you get the parent process, you may be able to take more actions. For example if the basename of the parent is sh, or bash you can open and read the history file then find occurrences of your app. That will show the full command that invoked it. Other applications may have similar history files.
Related
I'm trying to run command line arguments (specifically echo) through the exec family of functions. I can get the execv function to run if I write my own executable and run it, but if I try to run touch or echo it returns -1
#include <stdio.h>
#include <unistd.h> // exec functions
#include <sys/types.h> // pid_t
#include <sys/wait.h>
#define HIGH 1
#define LOW 0
int digitalWrite(int pin, short type) {
pid_t pid = fork();
if (pid == 0) {
printf("pid == %i\n", pid);
if (type == HIGH) {
char* args[] = {"echo", "1", ">", "/sys/class/gpio/gpio67/value", NULL};
int val = execv(args[0], args);
printf("ran function execl, %i\n", val);
} else {
printf("Unable to do anything but set pin to HIGH\n");
}
} else if (pid < 0) { // pid < 0
printf("fork failed\n");
}
wait(NULL);
}
int main() {
printf("Starting digitalWrite\n");
digitalWrite(0, HIGH);
printf("Completed digitalWrite()\n");
return 0;
}
Just for context here's my build:
$ gcc wiringbeagle.c
$ ./a.out
Starting digitalWrite
pid == 0
ran function execl, -1
Completed digitalWrite()
Completed digitalWrite()
$ ls
a.out wiringbeagle.c
The command echo 1 > /sys/class/gpio/gpio67/value runs fine in the terminal on it's own, and if I create a local file (i.e. touch tmpfile.txt) and try to run echo hi > tmpfile.txt it runs as expected in my command line but doesn't run in the program.
I must be not understanding something with execv, and any assistance would be greatly appreciated!
The first argument to execv is the file to be executed. Unlike your shell, execv does not search through the directories indicated by the PATH environment variable, so you need to give it the complete path to the executable. Unless there is an executable file called echo in your current working directory, execv("echo",...) will fail with a "file not found" error. (Use perror to get better error messages).
If you want to search for the executable as the shell does, use execvp. But note that your shell probably executes echo as a built-in command, so it won't be the same echo as your shell uses. In this case, that's fine.
Once you fix that, you will encounter a different problem. Since you are just invoking a command-line utility with arguments, rather than using a shell, the argument ">" is just an argument. It is the shell which handles redirections (as well as pipes, quoting, and a bunch of other useful stuff). So all you will accomplish is to send the three arguments to stdout.
You could use the system function to execute a command using the shell, or you could set up the redirection yourself by freopening stdout in your child before doing the execvp.
You can get quite a lot of information about system interfaces using the man command. For example, to learn what freopen does, use man freopen. You can also read manpages on the internet, eg. http://man7.org/linux/man-pages/man3/freopen.3.html, but the documentation on your own system is right there, and also applies to the actual version of the software installed on your system (assuming you installed the documentation).
I'm not entirely certain why you're even using the exec family to run external programs in this case. The C standard library provides perfectly adequate file I/O stuff.
For example, you can simply fopen, fprintf, and fclose the file without ever starting another external process to do that work for you:
int bytesWrit = 0;
FILE *gpioHndl = fopen("/sys/class/gpio/gpio67/value");
if (gpioHndl != NULL) {
bytesWrit = fprintf(gpioHndl, "1\n");
fclose(gpioHndl);
}
if (bytesWrit != 2) {
HandleError();
}
This is probably the preferred way to do what you want, which is simply writing a fixed value to a file.
In terms of why your execv call isn't working (though it's totally irrelevant if you take my advice above), there are several things you need to be aware of.
First, while some commands are actually files on the disk that you can exec, others may be internal bash commands(a). On my system, for example:
pax:~$ type ftp
ftp is /usr/bin/ftp
pax:~$ type echo
echo is a shell builtin
One way to solve this is to run the actual bash executable (which, being an on-disk command, can be done via exec), telling it to run its internal echo command. From the command line:
pax:~$ bash -c 'echo xyzzy'
xyzzy
Second, if want to use redirection, this is normally something that's done by the shell, not the exec calls or individual executables.
Trying to do redirection via the exec family will generally only result in the >somefile being passed as a literal parameter to the executable (in the argv array), not being used to attach standard output to a file. In other words, it won't work unless the executable specifically handles redirection, which is rare.
So that means you will have to run the shell with redirection and have it run the executable after performing those redirections, even if the command is not an internal one.
Thirdly, if you want the path searched for your executable, execvp is the call you want, not execv (the latter just uses the file you explicitly provide, either relative from the current working directory or an absolute path like /bin/ls). So, in your case, you should either:
use execvp to search the path; or
fully specify the path with execv.
(a) The echo command, while it is bash-internal may also be provided as a separate executable (I believe Posix requires this), so this may not be an issue here. It may be an issue if you expect them to act exactly the same in terms of more esoteric arguments :-)
execv() does not search the PATH environment variable in order to find an executable file. Per the Linux execv() man page (bolded text added):
...
Special semantics for execlp(), execvp(), and execvpe()
The execlp(), execvp(), and execvpe() functions duplicate the actions
of the shell in searching for an executable file if the specified
filename does not contain a slash (/) character. ...
...
So, those three will search the PATH environment variable if the filename passed does not contain a / character.
You're using execv(), which is not one of those three. Therefore, execv() will not search the PATH environment variable.
Since your current working directory doesn't contain an executable file called echo, execv() fails.
You need to use execvp() per the man page.
You need to use absolute path as first parameter in execv
Then, the correct is:
char* args[] = {"/bin/echo","echo", "1", ">", "/sys/class/gpio/gpio67/value", NULL};
But to run what you want (put value '1' in file '/sys/class/gpio/gpio67/value'), you need to use command sh:
char* args[] = {"/bin/sh", "sh","-c", "/bin/echo 1 > /sys/class/gpio/gpio67/value", NULL};
The parameter to "sh -c" is a string. Then, you need to put all command together as a string
I can open a new terminal by this code,
char *argv[]={"gnome-terminal"," -x ","/home/try/",NULL};
if(execvp(argv[0], argv)==-1){
printf("Error in receiver\n");
exit(EXIT_FAILURE);
}
This will open a terminal with path /home/try
I can open another program through
system("./xyz");
I have a program say, pqr.c, and i want to open a already existing program xyz.c through it, but i want the output of pqr and xyz to be displayed on two different terminal.
How to do this?
You can make use of your terminal program's own capabilities. Most (if not all) Unix terminal emulators can take an argument which tells them what program to run, instead of the user's shell.
gnome-terminal is no different here and also it uses the most common parameter for the task: -e.
So your code can look like:
char *argv[]={
"gnome-terminal",
"-x", "/home/try/", // <-- note: no space before or after "-x"
"-e", "/path/to/your/program",
NULL};
if(execvp(argv[0], argv)==-1){
printf("Error in receiver\n");
exit(EXIT_FAILURE);
}
Some things to note:
The terminal will close as soon as its inner program (/path/to/your/program) finishes execution. To prevent that, you can make the program wait for some input before termnating.
As noted in the code snippet, there should be no additional spaces around program arguments. If gnome-terminal works fine with them, this only means that it strips those spaces while parsing arguments.
I'm stuck on a piece of work which requires me to write a basic shell in C to be run on minix, following the pseudo code below (1). This is my first time using C and at the minute its all a little over my head. Ive just about managed to write the first part of the program (prompting user for input, then parsing the input into tokens to break down into commands and arguments). But i am struggling with then executing the command.
I'm not looking for someone to just do the work for me and then give me the answer, what I really need is help to understand what it is that I need to do to complete the task (i.e. a general outline, resources I can use, etc.)
Any help would be greatly appreciated!
*Following suggestions that the question is too broad, I would like to point out that the main thing i'm having trouble with here is understanding the 'execve' function (even after reading the man page)
(1)
#define TRUE 1
/* declare cmd, params, envp, stat, prompt, readcmd */
while (TRUE) { /* repeat forever */
prompt(); /* display prompt */
readcmd(cmd, params); /* read input from terminal*/
if (fork() != 0) { /* fork child process */
/* parent code */
waitpid(-1, &stat, 0); /* wait for child */
} else {
/* child code */
execve(cmd, params, envp); /* execute command */
}
}
}
As mentionned in the comments below your question, if this is the first time you're using C, you should be learning how to write a basic Hello World program and not how to code a unix shell from scratch !
But since you asked, I'm going to give you some guidelines with the fragment of code you already have :-)
First of all, it is very bad practice to make blind calls to fork() and execve() without even checking if your input is a valid unix command.
You need to check first if the input is a command that can actually be executed. For this, you will have to locate (if it exists) the binary of your input command. For example, the ls command is actually a binary located in /bin/ls. Luckily for Unix users, a variable named PATH and stored in the environnement will inform the shell where to look for binaries.
$> env will print you all the variables stored in the environnement. By doing this, you will see a line that pretty much looks like this depending on each operating system:
PATH=/usr/lib/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
This line contains several paths concatenated with a colon ":" These paths correspond to the folders where common system binaries are stored.
So if the command you want to execute exist, it's binary should be located in one of those folder !
As a third argument of your main function, you can retrieve the char **env in your C program. Iterate over it until you find the PATH line.
Split this line into a list of paths
Iterate over this list and for each path, append your input command at the end. Then, pass it to the very handy stat() function that will give you several usefull informations (if the file exists, if it has execution rights...). Reading man 2 stat would also help you a lot in this exercise by the way
Over your iteration, if one the created paths exists and has execution rights, save this path and execute it later with execve(). Otherwise, if none of the created paths exists (stat() returning -1) or doesn't have execution right, the input is probably not a command. You should also learn about builtins http://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#Bourne-Shell-Builtins
The $> which <command> is very usefull to locate existing commands or aliases
That should help you through the process but then again, I would also advise you to do several tutorials on fork() and execve().
Best of luck !
I am trying to execute a program from within a C program (inside UNIX).
I have been given an executable ( the program requires a string input during execution and writes that input to another file called sample ) called exec and I want to execute it in program.c, but giving the string input through indirection.
For that I created a file as follows:
% vim input
I wrote the following inside the input file
content
Now in program.c,
#include<unistd.h>
int main()
{
const char* command = "./exec < input";
execvp(command, NULL);
return 0;
}
When I run the program, the content is not entered into the sample file.
But when I run it without indirection, i.e.
const char* command = "./exec";
then it works, and input entered in saved in sample file.
Can someone please tell what am I doing wrong in the indirection syntax.
Thanks.
The syntax you are using is supposed to be interpreted by a shell like bash, csh, ksh, etc.
The system call execvp only expects the path to the executable and a number of arguments, the shell is not invoked there.
To perform redirection in this manner, you'll have to use the dup2(2) system call before calling execvp:
int fd = open("input", O_RDONLY);
/* redirect standard input to the opened file */
dup2(fd, 0);
execvp("/path/to/exec", ...);
Of course, you'll need some additional error checking in a real-world program.
You can't do redirection like that with execvp. Use system() or start getting friendly with dup() and friends. You might google 'implementing redirection'.. you'll likely turn up plenty of examples of how shells (for example) handle this problem.
The exec(3) family of functions does not know anything about input redirection or parsing command lines: it tries to execute exactly the executable you give it. It's trying to search for an executable file with the name "./exec < input", which unsurprisingly does not exist.
One solution would be to use the system(3) function instead of exec. system invokes the user's shell (such as /bin/bash), which is capable of parsing the command line and doing appropriate redirections. But, system() is not as versatile as exec, so it may or may not be suitable for your needs.
The better solution is to do the input redirection yourself. What you need to do us use open(3) to open the file and dup2(3) to duplicate the file descriptor onto file descriptor 0 (standard input), and then exec the executable.
I have some problem to figure out how I can maintain the pipe and redirect functionality of a shell once I find out that there are missing command line arguments.
If I for example use a scanf call, that will work with a re-direct or a pipe from a shell, but in absence of this I get a prompt, which I don't want.
I would like to accept command line arguments through argv[], a pipe or re-direct but I can't figure out how to do it with out getting the prompt. If I for example try something like this:
if(argc < 2)
exit(0);
Then the program will terminate if I try this:
echo arg | myProgram
Or this:
myProgram < fileWithArgument
I have tried to look this up but I always get some bash scripting reference.
The common way to handle situations like this is to check if the standard input stream is connected to a terminal or not, using isatty or similar functions depending on your OS. If it is, you take parameters from the command line, if not (it's been redirected), you read standard input.
Short version: You can't do it.
Pipeline and redirect specifiers are not arguments to your program, rather they are commands to the invoking shell and are processed before the running instance of your program even exists. The shell does no pass them to the program in argv or any other variable, and you can not discover them in any reliable way.
Neil has given you the way to determine if you are connected to a terminal.
In your examples you are using pipe redirection, both echo arg | myProgram and myProgram < filesWithArguments are sending output to the STDIN of your program.
If you want to read these values, use scanf or fread on the STDIN file descriptor.
If you are trying to get the file content as an argument list for your executable, you need to use it like this:
# This will pass `lala` as a variable
myProgram `echo lala`