How to take argument from c and use it as a function - c

I would like to take an argument, that is given to me through the terminal and use it for zipping the files. For example, I have main.c file that is look something like that.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...
int main(int argc, char **argv)
{
if(argc > 2){
printf("Enough arguments\n");
// tisk argumentu
printf("%s\n\n", argv[1]);
// tisk argumentů
for (int i; i < argc; i++){
printf("%s\n", argv[i]);
}
}
else{
printf("Insufficient number of arguments\n");
}
return 0;
}
and I give it through the terminal these parameters (gzip text1.txt text2.txt) like this.
$ gcc main.c -o main
$ ./main gzip text1.txt text2.txt
My question is how to take the parameter gzip which is in argv[1] and apply this function to the text1.txt and text2.txt.
This is what I have now. Problem is, that execlp takes place only once and I wanted to make this possible for all arguments (text1.txt, text2.txt --> later for more than two) now I am able to make it possible only for one bcs I hard code it there (argv[2]) --> that is the text1.txt and I need to make it possible for n files. Is there someone who will be able to answer the question "For what I should replace the argv[2] to gzip all files instead only one?"
#include <sys/types.h>
// we use unistd (in windows is process.h) library that allows us to execute program (such as gzip, ping etc) inside our program
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...
int main(int argc, char* argv[])
{
if(argc > 2){
printf("Enough arguments\n");
// printing second argument
printf("Program we use to compress: %s\n\n", argv[1]);
// printing all arguments
for (int i = 0; i < argc; i++){
printf("%d.argument: %s\n", i, argv[i]);
}
int tmp;
for (int i = 0; i < argc + 1; i++){
if (i < 2){
tmp ++;
}
else{
fork();
// execl (we use execlp to avoid defining path to pragram, or execvp where v stants for vector to pass more parametrs as argument) has firt two argument defined as program we want to execute to executables that we've sort as arguments to the reminal
execlp(
argv[1],
argv[1],
argv[2],
NULL
);
// if the program lands here we've got a problem because something went wrong so we use library errno to define an error that occurs
int err = errno;
if (err == 2){
printf("File with that name not found or path to that file was wrong!\n");
break;
}
}
}
}
else{
printf("Insufficient number of arguments\n");
}
return 0;
}
po úpravě
#include <sys/types.h>
// we use unistd (in windows is process.h) library that allows us to execute program (such as gzip, ping etc) inside our program
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...
int main(int argc, char* argv[])
{
if(argc > 2){
printf("Enough arguments\n");
// printing second argument
printf("Program we use to compress: %s\n\n", argv[1]);
// printing all arguments
for (int i = 0; i < argc; i++){
printf("%d.argument: %s\n", i, argv[i]);
}
for (int i = 2; i < argc + 1; i++){
// execl (we use execlp to avoid defining path to pragram, or execvp where v stants for vector to pass more parametrs as argument) has firt two argument defined as program we want to execute to executables that we've sort as arguments to the reminal
execlp(
argv[1],
argv[1],
argv[i],
NULL
);
// if the program lands here we've got a problem because something went wrong so we use library errno to define an error that occurs
int err = errno;
if (err == 2){
printf("File with that name not found or path to that file was wrong!\n");
break;
}
}
}
else{
printf("Insufficient number of arguments\n");
}
return 0;
}

The execv function takes an array of char *, the last of which must be NULL. The argv array fits this definition, so you can pass in the address of argv[1] which gives you the command specified and its parameters.
Also, you should check the return value of fork and only call execv in the child process.
if (fork() == 0) {
// child
execv(argv[1], &argv[1]);
perror("exec failed");
_exit(1);
} else {
// parent
wait(NULL);
}

There it is! Finall working solution
#include <sys/types.h>
// we use unistd (in windows is process.h) library that allows us to execute program (such as gzip, ping etc) inside our program
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
// gzip text1 = 1st process, gzip text2 = 2nd process, gzip text3 = 3rd ...
int main(int argc, char* argv[])
{
if (argc > 2){
printf("Enough arguments\n");
// printing second argument
printf("Program we use to compress: %s\n\n", argv[1]);
// printing all arguments
printf("Arguments we use:\n");
for (int i = 0; i < argc; i++){
printf("%d.argument: %s\n", i, argv[i]);
}
// we start our loop from third argument
for (int i = 2; i < argc + 1; i++){
// splitting our program to n processes
int id = fork();
if (id == 0){
// execl (we use execlp to avoid defining path to pragram, or execvp where v stants for vector to pass more parametrs as argument) has firt two argument defined as program we want to execute to executables that we've sort as arguments to the reminal
execlp(
argv[1], // program we use
argv[1], // program we use
argv[i], // the document to which the program is applied
NULL
);
// if the program lands here we've got a problem because something went wrong so we use library errno to define an error that occurs
int err = errno;
if (err == 2){
printf("File with that name not found or path to that file was wrong!\n");
break;
}
}
}
}
else{
printf("Insufficient number of arguments\n");
}
return 0;
}

Related

Create N processes in Linux and create a file in the current directory for every process

My homework is to execute a C program from Linux shell with N parameters: N file names.
I have to create N processes and for each process I have to create a .bak file with the file name specified in the parameters.
The professor suggests we use the cp command but I don't know how to proceed.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
typedef char stringa[80];
typedef stringa strvett[DIM];
void usage(char* prog_name) {
fprintf(stderr,"Usage:\n\t%s: file1 file2 ... fileN dir_dest.\n", prog_name);
}
void main(int argc, char *argv[]) {
int i = 0, j = 0, status, N = argc - 2, n_children = N;
int[N] pid;
char[N]* files;
char[PATH_MAX - 1] wd;
char term = '0';
if (strcmp(argv[argc - 1], getcwd(wd, sizeof(wd)))) {
fprintf(stderr, "Invalid directory.\n");
usage(argv[0]);
exit(EXIT_FAILURE);
}
for (i = 0; i < N; i++) {
char[strlen(argv[i + 1])] files[i];
strcpy(files[i], argv[i + 1]);
}
for (j = 0; j < n_children; j++ ) {
pid[j] = fork();
if (pid[j] == 0) { // Executed by child
if (term == '1') exit(0);
else {
term = '1';
execl("/bin/ls", "ls", "-l", (char*)0);
// cp command???
}
}
else {
if (pid[j] > 0) printf("%d: child created with PID %d\n", getpid(), pid[j]); // Executed by parent
else {
perror("Fork error");
exit(1);
}
}
}
}
The below program forks child processes and each process create a .bak file, using cp command, with the same name as a file name specified in the argument list passed to program. Since, the last argument is dir_dest, I am assuming that it is the destination directory where the child processes are suppose to create .bak files. Follow the inline comments of program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define FL_EXTN ".bak"
#define PLEN 256
#define NLEN 64
void usage(const char* prog_name) {
printf("Usage:\n\t%s: file1 file2 ... fileN dir_dest.\n", prog_name);
}
int main(int argc, char *argv[]) {
char arg1[NLEN];
char dir_dest[PLEN];
// adding check for argc < 3, as it need minimum 2 arguments
// a file_name and dir_dest
if (argc < 3) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
// add the command line argument validations
// for e.g. dir_dest is valid or not
strcpy (dir_dest, argv[argc - 1]);
// run loop for argc - 2 time as the first argument is process name and
// last is dir_dest, rest are files to be copy with .bak extension
for (int j = 0; j < argc - 2; j++) {
// leaving it up to you to add a check whether file argv[j+1]
// exits or not. Below code is based on assumption that files exist
strcpy (arg1, argv[j+1]);
// fork child process
pid_t child_pid = fork();
if (child_pid == 0) {
// in child process
char *cmdpath = "/bin/cp";
char *cmd = "cp";
char arg2[PLEN];
// prepare the .bak file absolute path using dir_dest
snprintf (arg2, PLEN, "%s/%s%s", dir_dest, arg1, FL_EXTN);
// replace child process with cp
execl(cmdpath, cmd, arg1, arg2, NULL);
// get here only if execl fails
perror("execl failed");
// exit child process
exit(1);
} else {
// in parent process
if (child_pid > 0) {
printf("%d: child created with PID %d\n", getpid(), child_pid);
} else {
perror("Fork error");
exit(1);
}
}
}
return 0;
}
Additional:
Using void as return type of main function is not as per standards. The return type of main function should be int.

Attempting to call execvp to run programname supplied through stdin, but call fails each time

Good evening
I have found a few similar questions, but nothing that suffices based on this specific question.
I am forking a child process, and attempting to call execvp to run a simple program which outputs 3 chars to stdout. The program name to be run comes from user input.
For some reason every call to execvp fails for simpleO:
I compile file simpleO.c into simpleO, and then compile and run fork.c. I type simpleO into the prompt when requested to attempt to run, but each time I get the error. Here is the code.
The error message printed by perror is "No such file or directory"
--
fork.c
#include <stdio.h>
#include <unistd.h>/*fork, exec*/
#include <errno.h>/*errno*/
#include <string.h> /*strerror*/
#include <stdlib.h>/*EXIT_FAILURE*/
#define BUFFERSIZE 100
int main(int argc, char *argv[]){
int i = 0;
pid_t pid;
int status;
char buffer[BUFFERSIZE];
fgets(buffer, BUFFERSIZE, stdin);
argv[0] = strtok(buffer, " ");
while (buffer[i] != '\0') {/*analyze each char*/
if (buffer[i] == ' ')/*based on how many spaces there are*/
argv[i] = strtok(NULL, " ");/*tokenize, populate argv*/
i++;
}
if((pid = fork()) == -1){
fprintf(stderr, "fork error: %s\n", strerror(errno));
return EXIT_FAILURE;}
else if (pid==0) {
int e = execvp(argv[0],argv);
if (e==-1) {
perror("Exec failed");
printf("Process %s\n",argv[0]);
perror("Process did not run");
exit(1);
}
}
else{
wait(&status);}
return 0;
}
--
simpleO.c
#include <stdio.h>
int main(int argc, char **argv){
printf("%c",'c');
printf("%c",'2');
printf("%c",'1');
return 0;
}
Addl ref: perror prints "No such file or directory"
Okay, look: a key lesson in programming is that when an error message tells you something, you should believe it. So, your program is saying "no such file or directory", and it means the file you're trying to run doesn't exist at whatever path you're trying to use.
You now know that it's not at the default path, so the next thing to try is to use the absolute path of the executable. Now, given the rest of this, I'd suggest you write a very simple C program that does nothing whatsoever besides trying to fork/exec your child program. No argv vector or anything, just fork and then execvp in the child with the executable path an absolute parth.

Making simple unix shell using fork,execvp

printf(*arg);
execvp(*arg, arg);
Here printf() statement prints value= ls.But when running program execvp gives there is no such file or directory.
else if (pid == 0) {
printf(*arg);
execvp(*arg, arg);
char* error = strerror(errno);
printf("shell: %s: %s\n", arg[0], error);
return 0;
if(execvp(arg[0], arg)<0)
{
printf("***ERROR: execution failedn\n");
}
return 0;
}
In the following code are two examples of how to use execvp.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *args[] = {"ls", "-l", NULL};
/* an example with a decleared array containing the commande */
if (fork() == 0) {
execvp(args[0], args);
}
/* another example where the commande was passed to main */
printf("argv[0] is the programme/executable name!\nargv[0] = %s\n", argv[0]);
if (fork() == 0) {
execvp(argv[1], argv + 1);
}
return EXIT_SUCCESS;
}
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.

execve won't run executable assembly file

I'm trying to create a c program that takes an executable and its arguments and runs them using execve, and then does some other stuff that shouldn't matter. The problem I'm having is that execve won't work when calling it on an exectuable assembly file. I think the problem is with my path because I can get the unix shell commands to work, but I can't get executables in the current directory (using ./spy ./executableName where spy is the name of my c program) to run. Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
extern char **environ;
int main(int argc, char* const argv[]) {
pid_t pid;
char filename[50];
char* arglist[argc];
int i = 1,count = 0;
int status;
strcpy(filename, "/bin/");
strcat(filename,argv[1]);
for(i = 1; i< argc; i++)
arglist[i-1] = argv[i];
arglist[argc-1] = 0;
arglist[0] = filename;
if (argc == 1) {
fprintf(stderr,"usage : %s <prog> ...\n",argv[0]);
return -1;
}
pid = fork();
if(pid == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
if(execve(filename, arglist, 0) < 0)
fprintf(stdout,"Invalid file.");
}
else {
while(1) {
waitpid(pid,&status,0);
if (WIFEXITED(status))
break;
ptrace(PTRACE_SINGLESTEP, pid,NULL, NULL);
count++;
}
}
return 0;
}
From the source you posted it looks as if you were always prefixing the name passed as parameter with /bin/. So if the file isn't in /bin/ it can not be found, nor run.
Just change these two lines:
strcpy(filename, "/bin/");
strcat(filename,argv[1]);
to be:
strcpy(filename,argv[1]);
Note that having applied this modification the program to be run needs to be specified with its full path.
So to run ls you need to do specfify /bin/ls as parameter to the program.
Some other comments:
So avoid buffer a overflow for long path/file names change:
char filename[50];
to be:
char filename[PATH_MAX];
To get more detailed information on why an execve() might have failed change:
if(execve(filename, arglist, 0) < 0)
fprintf(stdout,"Invalid file.");
to be:
if(execve(filename, arglist, (char*) 0) < 0)
perror("execve() failed");
To detect a possible failure of forking do change:
pid = fork();
to become:
if (-1 == (pid = fork())) then
{
perror("fork() failed");
}
else

create a new process to execute ls command

I want to write a program which will create a new process and in that child process, it should execute the command: ls. In the meanwhile, the parent should wait for the child to die. However, my code does not work.
Please help me thank you very much!
int main()
{
char * read;
size_t size;
getline(&read , &size , stdin);
read[strlen(read)-1] = '\0';
printf("%s\n" , read);
int status;
pid_t f;
if((f = fork()) == 0)
{
execvp(read , &read);
exit(0);
}
else
{
wait(&status);
}
}
From man execvp:
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.
You need to use an array of char* and set the last element to NULL.
I am unsure what the getline() is reading but I guess it is the directory to be lsd. The first argument to execvp() should be ls and the second argument the array of char*.
Consider the following:
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
char *input_arg[2];
char *input_str = NULL;
size_t input_len = 0;
char **args;
ssize_t len;
size_t n;
pid_t child, p;
int status;
if (argc < 2) {
/* No command line parameters. Read command from stdin. */
len = getline(&input_str, &input_len, stdin);
/* Find length excluding the newline at end. */
if (len > (ssize_t)0)
n = strcspn(input_str, "\r\n");
else
n = 0;
if (n > (size_t)0) {
/* Terminate input command before the newline. */
input_str[n] = '\0';
} else {
fprintf(stderr, "No input, no command.\n");
return 1;
}
input_arg[0] = input_str;
input_arg[1] = NULL;
args = input_arg;
} else {
/* Use command line parameters */
argv[argc] = NULL;
args = argv + 1;
}
child = fork();
if (child == (pid_t)-1) {
fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
return 1;
}
if (!child) {
/* This is the child process. */
errno = ENOENT;
execvp(args[0], args);
fprintf(stderr, "%s: %s.\n", args[0], strerror(errno));
exit(127);
}
do {
p = waitpid(child, &status, 0);
} while (p == (pid_t)-1 && errno == EINTR);
if (p == (pid_t)-1) {
fprintf(stderr, "Lost child process: %s.\n", strerror(errno));
return 127;
}
if (p != child) {
fprintf(stderr, "waitpid() library bug occurred.\n");
return 127;
}
if (WIFEXITED(status)) {
if (!WEXITSTATUS(status))
fprintf(stderr, "Command successful.\n");
else
fprintf(stderr, "Command failed with exit status %d.\n", WEXITSTATUS(status));
return WEXITSTATUS(status);
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "Command died by signal %s.\n", strsignal(WTERMSIG(status)));
return 126;
}
fprintf(stderr, "Command died from unknown causes.\n");
return 125;
}
The above uses the command line parameters if specified, otherwise it reads one from the standard input. Because the standard input is not tokenized, you can only supply the command name, no parameters. If you enlarge the input_arg[] array into
char *input_arg[4];
and modify the assignment into
input_arg[0] = "/bin/sh";
input_arg[1] = "-c";
input_arg[2] = input_str;
input_arg[3] = NULL;
args = input_arg;
then the input string will be processed using the /bin/sh shell, just like popen() does.
You can also use len = getdelim(&input_str, &input_len, '\0', stdin); and remove the input_str[n] = '\0'; assignment to allow multiline input; the shell should handle those fine, as long as it is short enough to fit in the command line argument buffer (maximum length depends on your OS).
The rules how shells split input into separate commands and parameters are rather complex, and you should not try to emulate them. Instead, find a simple way for the user to specify the parameters separately (like the command-line parameter case), or use the shell to do it for you. If you don't do any splitting, you will probably need to remove the newline at the end of the input line.
The point to note is that for execvp(file, args), args[0] is the name the application sees (as $0 or argv[0]), and args[1] is the first parameter. Each parameter is terminated by NUL (\0) just like strings are normally in C, and the args pointer array must end with a NULL pointer. If there are no parameters, then args[1] == NULL.
why dont you just use system command...
#include <stdio.h>
#include <stdlib.h>
int main ()
{
int i;
printf ("Executing command ls...\n");
i=system ("ls");
printf ("The value returned was: %d.\n",i);
return 0;
}
Update:
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void main(void)
{
pid_t pid;
pid = fork();
if (pid == 0) // this is child process
{
int i;
printf ("Executing command ls...\n");
i=system ("ls");
printf ("The value returned was: %d.\n",i);
}
else // this is paraent process
{
int status=0
wait(&status);
printf ("Child process is returned with: %d.\n",status);
}
}

Resources