Reading commands from cmd line, and executing them in C - c

I'm writing a program to take user input from the command line (linux/unix commands), and execute them within my program.
My steps so far:
Ask user for number of commands input
Fork() to create child process
Output Child PID and Parent PID
Allow user to input each command, read each input into an index of argv
Use execv to run each command inside of argv
The main issue is that when it executes, it merely does the "bin/ls/" in the execv command.
Here is a sample output from running my program:
Enter number of commands: 2
Child's PID is 3487. Parent's PID is 3485
Enter a UNIX command: ls
Enter a UNIX command: -al
LIST OF FILES AS IF TYPING "LS" ON THE CMD LINE
Process Complete.
And here is my source code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
void main(int argc, char *argv[20])
{
int pid;
int num = 0;
printf("Enter number of commands: ");
scanf("%d", &argc);
pid = fork();
if(pid == 0)
{
printf("Child's PID is %d. Parent's PID is %d\n", (int)getpid(), (int)getppid());
for(num=0; num < argc; num++)
{
printf("Enter a UNIX command: ");
scanf("%s", argv[num]);
}
argv[num+1] = 0;
execv("bin/ls/", argv);
}
else
{
wait(pid);
printf("Process Complete.\n");
exit(0);
}
}

Firstly you are defining char* argv[20] in main which is not a good idea. If you ever pass in more than 20 arguments you will exceed the bounds of the array.
Secondly, you are attempting to read a string with scanf("%s", argv[num]) into an address space that is not initialized as far as I can tell.
The argv[] array of "strings" is initialized by the OS when your program is invoked and if you don't pass any arguments to your program you will not have any "strings", meaning that you will be writing to random memory which you might not own.
If you really want to load your commands the way you are doing it now please try the following:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
void main(int argc, char *argv[])
{
int pid;
int num = 0;
int argc2 = 0;
char* argv2[20]; // argv2 will point inside of buffer for convenience.
char* buffer[2000]; // note each array has a limit of 100 characters.
printf("Enter number of commands: ");
scanf("%d", &argc2);
pid = fork();
if(pid == 0)
{
printf("Child's PID is %d. Parent's PID is %d\n", (int)getpid(), (int)getppid());
for(num=0; num < argc2 && num < 20; num++) // your array is 20 long
{
argv2[num] = &buffer[num * 100];
printf("Enter a UNIX command: ");
scanf("%s", argv2[num]);
}
argv[num] = 0; // no need to add + 1 because the for loop did already anyway.
execv("Assignments/ls", argv2);
}
else
{
wait(pid);
printf("Process Complete.\n");
exit(0);
}
}
Alternatively you could just pass arguments to your main program which simply passes them onto the called program like so:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
void main(int argc, char *argv[])
{
int pid;
int num = 0;
printf("You entered %d commands: \n", argc);
for (num = 0; num < argc; ++num)
{
printf("\t%s\n", argv[num]);
}
pid = fork();
if(pid == 0)
{
printf("Child's PID is %d. Parent's PID is %d\n", (int)getpid(), (int)getppid());
execv("Assignments/ls", &argv[1]);
}
else
{
wait(pid);
printf("Process Complete.\n");
exit(0);
}
}

One specific problem your code has is you must pass argv[idx] as the argument to exec. You're passing an array of char pointers, by passing argv.
Please also be advised that argc contains the full count of arguments, and that full count includes the program itself. argv[0] contains the program name to which you are passing the arguments. I'm not seeing that being reflected in your for loop. That is you are processing your own program and running it.
The way I've written these is to traverse argv in a while (or for, if you prefer), using an int varaiable -- for example int idx=0; -- until I find an argv[idx] pointer that is null.
If, for example, you had three arguments, argc would be 4, and argv[3] would be your last argument to process. argv[4] would be null.
Based on some of the answers you've received, here's a discussion of execv and fork.

You have wrong logic. use fork just before execv
move execv (together with fork) out of the loop;
1st argument of the execv - is a path to the binary file to execute;
2nd - array of arguments to pass to the binary. Is this correct that you have in
the current directory the sub-directory named 'Assignments' and this
directory contains the executable named 'ls'? And, please, read 'man execv' carefully
Update:
Disregard points 1 and 2 above.
man execv:
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.

Related

Running h5dump from C [duplicate]

I want to execute another program within C code.
For example, I want to execute a command
./foo 1 2 3
foo is the program which exists in the same folder, and 1 2 3 are arguments.
foo program creates a file which will be used in my code.
How do I do this?
For a simple way, use system():
#include <stdlib.h>
...
int status = system("./foo 1 2 3");
system() will wait for foo to complete execution, then return a status variable which you can use to check e.g. exitcode (the command's exitcode gets multiplied by 256, so divide system()'s return value by that to get the actual exitcode: int exitcode = status / 256).
The manpage for wait() (in section 2, man 2 wait on your Linux system) lists the various macros you can use to examine the status, the most interesting ones would be WIFEXITED and WEXITSTATUS.
Alternatively, if you need to read foo's standard output, use popen(3), which returns a file pointer (FILE *); interacting with the command's standard input/output is then the same as reading from or writing to a file.
The system function invokes a shell to run the command. While this is convenient, it has well known security implications. If you can fully specify the path to the program or script that you want to execute, and you can afford losing the platform independence that system provides, then you can use an execve wrapper as illustrated in the exec_prog function below to more securely execute your program.
Here's how you specify the arguments in the caller:
const char *my_argv[64] = {"/foo/bar/baz" , "-foo" , "-bar" , NULL};
Then call the exec_prog function like this:
int rc = exec_prog(my_argv);
Here's the exec_prog function:
static int exec_prog(const char **argv)
{
pid_t my_pid;
int status, timeout /* unused ifdef WAIT_FOR_COMPLETION */;
if (0 == (my_pid = fork())) {
if (-1 == execve(argv[0], (char **)argv , NULL)) {
perror("child process execve failed [%m]");
return -1;
}
}
#ifdef WAIT_FOR_COMPLETION
timeout = 1000;
while (0 == waitpid(my_pid , &status , WNOHANG)) {
if ( --timeout < 0 ) {
perror("timeout");
return -1;
}
sleep(1);
}
printf("%s WEXITSTATUS %d WIFEXITED %d [status %d]\n",
argv[0], WEXITSTATUS(status), WIFEXITED(status), status);
if (1 != WIFEXITED(status) || 0 != WEXITSTATUS(status)) {
perror("%s failed, halt system");
return -1;
}
#endif
return 0;
}
Remember the includes:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
See related SE post for situations that require communication with the executed program via file descriptors such as stdin and stdout.
You can use fork() and system() so that your program doesn't have to wait until system() returns.
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char* argv[]){
int status;
// By calling fork(), a child process will be created as a exact duplicate of the calling process.
// Search for fork() (maybe "man fork" on Linux) for more information.
if(fork() == 0){
// Child process will return 0 from fork()
printf("I'm the child process.\n");
status = system("my_app");
exit(0);
}else{
// Parent process will return a non-zero value from fork()
printf("I'm the parent.\n");
}
printf("This is my main program and it will continue running and doing anything i want to...\n");
return 0;
}
system() executes a shell which is then responsible for parsing the arguments and executing the desired program. To execute the program directly, use fork() and exec() (which is what system() uses to execute the shell as well as what the shell itself uses to execute commands).
#include <unistd.h>
int main() {
if (fork() == 0) {
/*
* fork() returns 0 to the child process
* and the child's PID to the parent.
*/
execl("/path/to/foo", "foo", "arg1", "arg2", "arg3", 0);
/*
* We woundn't still be here if execl() was successful,
* so a non-zero exit value is appropriate.
*/
return 1;
}
return 0;
}
In C
#include <stdlib.h>
system("./foo 1 2 3");
In C++
#include <cstdlib>
std::system("./foo 1 2 3");
Then open and read the file as usual.
How about like this:
char* cmd = "./foo 1 2 3";
system(cmd);
Here's the way to extend to variable args when you don't have the args hard coded (although they are still technically hard coded in this example, but should be easy to figure out how to extend...):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int argcount = 3;
const char* args[] = {"1", "2", "3"};
const char* binary_name = "mybinaryname";
char myoutput_array[5000];
sprintf(myoutput_array, "%s", binary_name);
for(int i = 0; i < argcount; ++i)
{
strcat(myoutput_array, " ");
strcat(myoutput_array, args[i]);
}
system(myoutput_array);

write() and read() binary numbers with processes

I am doing this homework for some time and it's giving me a headache.
Write a program that writes the integer “i+1” into element “i” of a table of MAXBUF
integers (for every element of the table). MAXBUF should be initially “#define”d as 10 in the source
code of the program. Then, using only one write() operation, the program should write the entire
table of integers in binary format into an initially truncated file, named “filetable.bin”. In the next
step the program should create a child process, and then print the message “The parent process
is terminating.”, and then exit. The child process should separately read, in binary format, from
the file each integer in the same order as the integers are stored in the file, and print each such
integer to the standard output. In the final step of the program, the child process should wait for its
parent process to terminate, and then print to the standard output the message “The child process
is terminating.”, and then terminate. All the operations on the “filetable.bin” file should be
performed using system calls.
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#define MAXBUF 10
int decimalToBinary(int n);
int main(int argc, char *argv []) {
int i, fd, n, status, childpid;
char buffer[MAXBUF];
pid_t pid = (long)getpid();
fd = open("filetable.bin", O_CREAT | O_RDWR);
for(i=0; i<MAXBUF; i++) {
n=i+1;
buffer[i] = decimalToBinary(n);
}
write(fd,buffer,sizeof(buffer));
childpid = fork();
if(childpid >0) {
printf("The parent process terminating.\n");
exit(0); // or kill(pid, SIGKILL)
}
if(childpid < 0) {
perror("Failed to fork\n");
}
else {
read(fd,&childpid,sizeof(childpid));
write(STDOUT_FILENO,&childpid,sizeof(childpid));
wait(&status);
printf("The child process is terminating\n");
exit(1);
}
return(0);
}
int decimalToBinary(int n) {
int remainder, binary=0, i=1;
while(n!=0) {
remainder = n%2;
n = n/2;
binary = binary + (remainder*i);
i = i*10;
}
return binary;
}
My problem is how do I read from the childpid and write with it in STDOUT?
EDIT: It appears in the output: "The parent process is terminating\n" "The child process is terminating\n". It's missing the STDOUT

C - Junk characters on output

Let's say, I have two programs - input.c & output.c
All I want to do is send some payload/characters in "half pyramid" format into another one using execl() function.
input.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define SIZE 1024
int length;
int main(int argc, char *argv[])
{
pid_t pid;
char *target;
//char payload[length+1];
char payload[SIZE + 1];
int status;
int i = 0;
if(argc < 2)
{
printf("Usage %s <length> <target>\n", argv[0]);
exit(EXIT_FAILURE);
}
length = atoi(argv[1]);
target = argv[2];
while(i < length)
{
pid = fork();
if(pid != 0)
{
waitpid(-1, &status, 0);
//exit(0);
}
if(pid == 0)
{
payload[i] = 'A';
payload[i + 1] = '\0';
execl(target, target, payload, NULL);
//printf("%s\n", payload);
}
++i;
}
return 0;
}
Commented passages are just for debugging purposes. Because As you can see (while trying), when you want just print it, everything works properly.
output.c (or if you want 'target.c')
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buffer[30];
strncpy(buffer, argv[1], sizeof(buffer));
printf("Output: %s\n", buffer);
return 0;
}
When I compile input.c like:
gcc input.c -o input
& output.c:
gcc output.c -o output
Ok. Now, everything is prepared. Let's say, I'd like to send a payload - length 6
./input 6 ./output
but all I get on output is just this (or simply with another junks characters):
Output: A
Output: 0A
Output: 0,A
Output: 0,�A
Output: 0,�8A
Output: 0,�8�A
I tried so many things, but all of them failed and output was still the same, as you can see above.
I'd be very grateful if anyone can help me and possibly show me where is problem. Can be problem in using fork() and execl() functions together?
Got it, you should not update payload in the child block code...
Here's a fix (cannot test it now) :
while(i < length)
{
pid = fork();
payload[i] = 'A';
payload[i + 1] = '\0';
if(pid != 0)
{
waitpid(-1, &status, 0);
//exit(0);
}
if(pid == 0)
{
execl(target, target, payload, NULL);
//printf("%s\n", payload);
}
++i;
}
[removed unrelated sentence]
EDIT (additional explanations) : payload updating must be in both parent and child code. If you don't understand why, I can add more explanation.
EDIT2 (as requested). You want update payload for the next forked child process. In your code, all child code is replaced by execl() into target code. So the fork() is executed only by the first parent process (the root one).
You have to update payload by the first parent and make it accessible too all the children. Putting it into this block won't work either :
// block only executed by the first parent.
if(pid != 0)
{
waitpid(-1, &status, 0);
}
Therefore, You must update it in a place both accessible by the parent and the child : after the fork(), before if(pid == 0) block.
In your code, you increment i in the common block, but the parent's payload is never updated. So in the child block, just before the execl(), your adding 'A\0' at the end of an uninitialized C string.
When your program forks, it creates a new process. This new process, after if(pid == 0), alters the payload, and runs exec (ie executes output, which then dies. I.e, your change of payload stays in the child, and does not affect the parent process. Only ++i does, which is why you're seeing unitialized data.
Move payload change before fork (or at least out of child-only block), so it's in the parent as well.

how to pass command line arguments to a child process after fork()

I have the following code draft.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
printf( "usage: %i filename", argc );
pid_t pID = fork();
if (pID == 0) // child
{
// Code only executed by child process
printf("Child PID: %i", pID);
int file = open("/tmp/rtail", O_CREAT | O_WRONLY);
//Now we redirect standard output to the file using dup2
dup2(file,1);
char tmp[30];
sprintf(tmp, "cat `tail -f %s`", argv[1]);
}
else if (pID < 0) // failed to fork
{
printf("Failed to fork");
exit(1);
// Throw exception
}
else // parent
{
}
// Code executed by both parent and child.
return 0;
}
How do I pass command line arguments to a child process? For example, running ./app alch.txt I want
sprintf(tmp, "cat `tail -f %s`", argv[1]);
to produce
cat `tail -f alch.txt`
in tmp.
How do I pass command line arguments to a child process?
You don't need to do anything special; fork ensures that each process gets all local variables, including argv.
Sorry for the trouble, it indeed works fine. My earlier version didn't work for some reason, but apparently I've changed something to make it right. Will run my code before a question next time.

passing command line arguments to a child process and count them

I want the parent process to take the arguments to main() and send the characters in them one at a time to the child process through a pipe starting with argv[1] and continue through the rest of the arguments.(one call to write for each character).
I want the child process to count the characters sent to it by the parent process and print out the number of characters it received from the parent. The child process should not use the arguments to main() in any way whatsoever.
What am i doing wrong? do i need to use exec()?
output that isnt correct:
~ $ gc a03
gcc -Wall -g a03.c -o a03
~ $ ./a03 abcd ef ghi
child: counted 12 characters
~ $
here is the program..
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int length = 0;
int i, count;
int fdest[2]; // for pipe
pid_t pid; //process IDs
char buffer[BUFSIZ];
if (pipe(fdest) < 0) /* attempt to create pipe */
printf("pipe error");
if ((pid = fork()) < 0) /* attempt to create child / parent process */
{
printf("fork error");
}
/* parent process */
else if (pid > 0) {
close(fdest[0]);
for(i = 1; i < argc; i++) /* write to pipe */
{
write(fdest[1], argv[i], strlen(argv[1]));
}
wait(0);
} else {
/* child Process */
close(fdest[1]);
for(i = 0; i < argc; i++)
{
length +=( strlen(argv[i])); /* get length of arguments */
}
count = read(fdest[0], buffer, length);
printf("\nchild: counted %d characters\n", count);
}
exit(0);
}
You said that "the child process should not use the arguments to main() in any way whatsoever". However, I see that your child process is using argc. Doesn't this defeat your restriction?
You also say that you want "one call to write for each character". Your current implementation uses one call to write for each argument, not each character. Was this a typo? If not, you will want to use something more like this:
char nul='\0', endl='\n';
for (a=1; a < argc; ++a) {
for (c=0; c < strlen(argv[a]); ++c) {
write(fdest[1], &argv[a][c], 1);
}
write(fdest[1], &nul, 1);
}
write(fdest[1], &endl, 1);
This will write one character at a time, with each argument as a NULL-terminated string and a newline character at the end. The newline is only there to serve as a marker to indicate that there is no more data to send (and is safe to use since you won't be passing in a newline in a CLI argument).
The child process will just need to be a loop that reads incoming bytes one by one and increments a counter if the byte is not '\0' or '\n'. When it reads the newline character, it breaks out of the input processing loop and reports the value of the counter.
You have an error here:
write(fdest[1], argv[i], strlen(argv[1]));
You should take strlen(argv[i]) rather, or you're telling write() to read past the space of argv[i] and invoke undefined behavior.
Note that you're only calling read() once. By the time you're calling read(), perhaps only one of the argv[]s have been written by the parent. Or 2. Or any number of them.
The problem is here
write(fdest[1], argv[i], strlen(argv[1]));
Notice that this is strlen of argv[1], it should be argv[i]. You are actually referencing past the end of argv[2] and argv[3] in this loop
You are effectively writing strlen("abcd") * 3 characters which is 12 chars
In here:
for(i = 1; i < argc; i++) /* write to pipe */
{
write(fdest[1], argv[i], strlen(argv[1]));
}
strlen(argv[1]) should be in fact strlen(argv[i])

Resources