Execute a Linux command in the c program - c

I am trying to execute a Linux command in c program using system system call, but the don't want it to dump the output or error logs on the terminal. What should I do? Is there any other way to do this?

As the system() call uses a shell to execute the command, you can redirect stdout and stderr to /dev/null, e.g.
system("ls -lh >/dev/null 2>&1");

popen is another way in which you can do the same:
void get_popen() {
FILE *pf;
char command[20];
char data[512];
// Execute a process listing
sprintf(command, "ps aux wwwf");
// Setup our pipe for reading and execute our command.
pf = popen(command,"r");
// Error handling
// Get the data from the process execution
fgets(data, 512 , pf);
// the data is now in 'data'
if (pclose(pf) != 0)
fprintf(stderr," Error: Failed to close command stream \n");
return;
}

Show you code.
Try for example:
system("ls");

The system() and popen() calls start a shell and pass their arguments to it, which creates security vulnerabilities. Unless all parts of the arguments originating from user input are correctly sanitized according to the shell's quoting and escaping rules, an attacker can probably run arbitrary commands on the system.
Instead, use the exec family of commands. These start the command directly, without starting a shell. You may still need to sanitize the input, but only to limit what may be passed to the command itself.
Example from the SEI CERT C Coding Standard:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
void func(char *input) {
pid_t pid;
int status;
pid_t ret;
char *const args[3] = {"any_exe", input, NULL};
char **env;
extern char **environ;
/* ... Sanitize arguments ... */
pid = fork();
if (pid == -1) {
/* Handle error */
} else if (pid != 0) {
while ((ret = waitpid(pid, &status, 0)) == -1) {
if (errno != EINTR) {
/* Handle error */
break;
}
}
if ((ret == 0) ||
!(WIFEXITED(status) && !WEXITSTATUS(status))) {
/* Report unexpected child status */
}
} else {
/* ... Initialize env as a sanitized copy of environ ... */
if (execve("/usr/bin/any_cmd", args, env) == -1) {
/* Handle error */
_Exit(127);
}
}
}

Related

Piping/dup2() not working properly (Implementing Unix Shell in C)

I'll post my code first, then explain the problem I'm having:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#define MAX_ARGS 20
#define BUFSIZE 1024
int get_args(char* cmdline, char* args[])
{
int i = 0;
/* if no args */
if((args[0] = strtok(cmdline, "\n\t ")) == NULL)
return 0;
while((args[++i] = strtok(NULL, "\n\t ")) != NULL) {
if(i >= MAX_ARGS) {
printf("Too many arguments!\n");
exit(1);
}
}
/* the last one is always NULL */
return i;
}
void execute(char* cmdline)
{
int pid, async, oneapp;
char* args[MAX_ARGS];
char* args2[] = {"-l", NULL};
int nargs = get_args(cmdline, args);
if(nargs <= 0) return;
if(!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) {
exit(0);
}
printf("before the if\n");
printf("%s\n",args[nargs - 2]);
int i = 0;
// EDIT: THIS IS WHAT WAS SUPPOSED TO BE COMMENTED OUT
/*
while (args[i] != ">" && i < nargs - 1) {
printf("%s\n",args[i]);
i++;
}
*/
// Presence of ">" token in args
// causes errors in execvp() because ">" is not
// a built-in Unix command, so remove it from args
args[i - 1] = NULL;
printf("Escaped the while\n");
// File descriptor array for the pipe
int fd[2];
// PID for the forked process
pid_t fpid1;
// Open the pipe
pipe(fd);
// Here we fork
fpid1 = fork();
if (fpid1 < 0)
{
// The case where the fork fails
perror("Fork failed!\n");
exit(-1);
}
else if (fpid1 == 0)
{
//dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
//close(fd[0]);
// File pointer for the file that'll be written to
FILE * file;
// freopen() redirects stdin to args[nargs - 1],
// which contains the name of the file we're writing to
file = freopen(args[nargs - 1], "w+", stdin);
// If we include this line, the functionality works
//execvp(args[0],args);
// We're done writing to the file, so close it
fclose(file);
// We're done using the pipe, so close it (unnecessary?)
//close(fd[1]);
}
else
{
// Wait for the child process to terminate
wait(0);
printf("This is the parent\n");
// Connect write end of pipe (fd[1]) to standard output
dup2(fd[1], STDOUT_FILENO);
// We don't need the read end, so close it
close(fd[0]);
// args[0] contains the command "ls", which is
// what we want to execute
execvp(args[0], args);
// This is just a test line I was using before to check
// whether anything was being written to stdout at all
printf("Exec was here\n");
}
// This is here to make sure program execution
// doesn't continue into the original code, which
// currently causes errors due to incomplete functionality
exit(0);
/* check if async call */
printf("Async call part\n");
if(!strcmp(args[nargs-1], "&")) { async = 1; args[--nargs] = 0; }
else async = 0;
pid = fork();
if(pid == 0) { /* child process */
execvp(args[0], args);
/* return only when exec fails */
perror("exec failed");
exit(-1);
} else if(pid > 0) { /* parent process */
if(!async) waitpid(pid, NULL, 0);
else printf("this is an async call\n");
} else { /* error occurred */
perror("fork failed");
exit(1);
}
}
int main (int argc, char* argv [])
{
char cmdline[BUFSIZE];
for(;;) {
printf("COP4338$ ");
if(fgets(cmdline, BUFSIZE, stdin) == NULL) {
perror("fgets failed");
exit(1);
}
execute(cmdline) ;
}
return 0;
}
So, what's the problem? Simple: the code above creates a file with the expected name, i.e. the name provided in the command line, which gets placed at args[nargs - 1]. For instance, running the program and then typing
ls > test.txt
Creates a file called test.txt... but it doesn't actually write anything to it. I did manage to get the program to print garbage characters to the file more than a few times, but this only happened during bouts of desperate hail mary coding where I was basically just trying to get the program to write SOMETHING to the file.
I do think I've managed to narrow down the cause of the problems to this area of the code:
else if (fpid1 == 0)
{
printf("This is the child.\n");
//dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
//close(fd[0]);
// File pointer for the file that'll be written to
FILE * file;
// freopen() redirects stdin to args[nargs - 1],
// which contains the name of the file we're writing to
file = freopen(args[nargs - 1], "w+", stdout);
// If we include this line, the functionality works
//execvp(args[0],args);
// We're done writing to the file, so close it
fclose(file);
// We're done using the pipe, so close it (unnecessary?)
//close(fd[1]);
}
else
{
// Wait for the child process to terminate
wait(0);
printf("This is the parent\n");
// Connect write end of pipe (fd[1]) to standard output
dup2(fd[1], STDOUT_FILENO);
// We don't need the read end, so close it
close(fd[0]);
// args[0] contains the command "ls", which is
// what we want to execute
execvp(args[0], args);
// This is just a test line I was using before to check
// whether anything was being written to stdout at all
printf("Exec was here\n");
}
More specifically, I believe the problem is with the way I'm using (or trying to use) dup2() and the piping functionality. I basically found this out by process of elimination. I spent a few hours commenting things out, moving code around, adding and removing test code, and I've found the following things:
1.) Removing the calls to dup2() and using execvp(args[0], args) prints the result of the ls command to the console. The parent and child processes begin and end properly. So, the calls to execvp() are working properly.
2.) The line
file = freopen(args[nargs - 1], "w+", stdout)
Successfully creates a file with the correct name, so the call to freopen() isn't failing. While this doesn't immediately prove that this function is working properly as it's written now, consider fact #3:
3.) In the child process block, if we make freopen redirect to the output file from stdin (rather than stdout) and uncomment the call to execvp(args[0], args), like so:
// freopen() redirects stdin to args[nargs - 1],
// which contains the name of the file we're writing to
file = freopen(args[nargs - 1], "w+", stdin);
// If we include this line, the functionality works
execvp(args[0],args);
and run the program, then it works and result of the ls command is successfully written to the output file. Knowing this, it seems pretty safe to say that freopen() isn't the problem either.
In other words, the only thing I haven't been able to successfully do is pipe the output of the execvp() call that's done in the parent process to stdout, and then from stdout to the file using freopen().
Any help is appreciated. I've been at this since 10 AM yesterday and I'm completely out of ideas. I just don't know what I'm doing wrong. Why isn't this working?

process loop in C

I'm trying to write a code about a process that executes programs from $PATH using the execlp() command.(it doesn't need to be the execlp command but I've found it useful for this one) I've achieved my expected output, but I need to run more than one commands. More specifically I want the child process to run the exec command, then the parent process to print a text indicating that it's ready to accept another command. Then the child process will run the new exec command. My code is this:
int main ( int argc, char *argp[]) {
pid_t progpid = fork(); // the fork command for the creation of the child process
int status = 0;
char com[256];
if (progpid < 0) // the check in case of failure
{
printf("PROGRAM ABORTED!");
return 0;
}
do
{
if (progpid == 0) // the child process
{
scanf( "%s", com);
if (com == "exit")
{
exit(0);
}
else
{
execlp(com, com, NULL);
}
}
else //the parent process
{
wait(&status);
printf("$");
}
}while (com != "exit");
return 0;
}
The expected output is :
<program which I input from keyboard> ( for example : ls )
<output of the program>
$<next program>
<output of the next program>
.
.
.
$exit
In short I want to keep running programs till I enter exit where it ends without doing anything else. However the output I get is this:
<program>
<output of program>
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
It keeps printing $ until I shut it down. I'm new to processes so please don't be too harsh about my code so far.
Thank you in advance!
This
if (com == "exit")
should be
if (strcmp(com, "exit") == 0)
Similarly change the while condition as well.
In C, string comparisons are done using strcmp(). == in your case, simply compares the address of com and the address of the string literal "exit". (In expressions, an array gets converted into a pointer to its first element. Hence, "address" comparison. Also see: What is array decaying?).
Note that your execlp() call has an issue. NULL may be defined as 0, in which case execlp(), being a variadic function, may be able to recognize it as the last argument.
I'd suggest to change it to:
execlp(com, com, (char*)0);
You'd also want to check if wait() failed or not by checking its return code.
Here's a simple example based on yours with improved error checking.
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main ( int argc, char *argp[]) {
for(;;) {
char com[1024];
printf("$ ");
fgets(com, sizeof com, stdin);
com[strcspn(com, "\n")] = 0; /* Remove if there's a newline at the end */
if (strcmp(com, "exit") == 0) {
exit(0);
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child process */
execlp(com, com, (char*)0);
}
int status;
int rc = wait(&status);
/* You can inspect 'status' for further info. */
if (rc == -1) {
perror("wait");
exit(1);
}
}
return 0;
}
Note that if you want your to execute commands with arguments then you need to do argument processing.

Hide terminal output from Execve

I'm making a C program where it basically reads in a line from the user, interprets it and then tries to execute the command with execve. I'm also forking the execve to a child process if '&' is in the input.
Now I wish to hide any terminal output which comes from the execve command when it's running in the child process.
Is there any relatively easy way to do this?
You can hide the output by redirecting stdout and stderr to /dev/null after forking but before execve(). The idea is to open /dev/null, then make stdout and stderr duplicates of the obtained file descriptor with dup2() (which will also close the originals first). It's almost the same as redirecting to a pipe.
An example (incomplete program, and skipping most error checking):
#include <unistd.h>
#include <fcntl.h>
...
int pid = fork();
if (pid == -1) {
/* fork error */
exit(1);
} else if (pid == 0) {
/* child process */
/* open /dev/null for writing */
int fd = open("/dev/null", O_WRONLY);
dup2(fd, 1); /* make stdout a copy of fd (> /dev/null) */
dup2(fd, 2); /* ...and same with stderr */
close(fd); /* close fd */
/* stdout and stderr now write to /dev/null */
/* ready to call exec */
execve(cmd, args, env);
exit(1);
} else {
/* parent process */
...
I've written simple example, maybe it will help you.
First, try to call it without | echo $1 > /dev/null - it should print files. When you add it, output is empty.
#include <stdio.h>
#include <unistd.h>
int main()
{
int ret;
char *cmd[] = { "ls", "-l", (char *)0 };
char *env[] = {(char *)0 };
ret = execve ("/bin/ls | echo $1 > /dev/null", cmd, env);
return 0;
}

Communicate with child process stdout/stdin

I am trying to communicate with a process (that itself writes to stdin and stdout to interact in a terminal with a user) and read it's stdin and write to it's stdout in C.
Hence I try to substitute a shell user programmatically. A methapohrical example: Imagine I want to use VIM in C for some reason. Then I also need to write commands (stdout) and read stuff from the editor (stdin).
Initially I thought this might be a trivial task, but it seems like there's no standard approach. int system(const char *command); just executes a command and sets the commands stdin/stdout to the one of the calling process.
Because this leads nowhere, I looked at FILE *popen(const char *command, const char *type); but the manual pages state that:
Since a pipe is by definition unidirectional, the type argument may specify only reading or writing, not both; the resulting stream is correspondingly read-only or write-only.
and its implication:
The return value from popen() is a normal standard I/O stream in all respects save that it must be closed with pclose() rather than fclose(3). Writing to such a stream writes to the standard input
of the command; the command's standard output is the same as that of the process that called popen(), unless this is altered by the command itself. Conversely, reading from a "popened" stream reads
the command's standard output, and the command's standard input is the same as that of the process that called popen().
Hence it wouldn't be completely impossible to use popen(), but it appears to me very inelegant, because I would have to parse the stdout of the calling process (the code that called popen()) in order to parse data sent from the popened command (when using popen type 'w').
Conversely, when popen is called with type 'r', I would need to write to the calling's process stdin, in order to write data to the popened command. It's not even clear to me whether both these processes receive the same data in the stdin in this case...
I just need to control stdin and stdout of a program. I mean can't there be a function like:
stdin_of_process, stdout_of_process = real_popen("/path/to/bin", "rw")
// write some data to the process stdin
write("hello", stdin_of_process)
// read the response of the process
read(stdout_of_process)
So my first question: What is the best way to implement the upper functionality?
Currently I am trying the following approach to communicate with another process:
Set up two pipes with int pipe(int fildes[2]);. One pipe to read the stdout of the process, the other pipe to write to the stdin of the process.
Fork.
Execute the process that I want to communicate with in the forked child process using int execvp(const char *file, char *const argv[]);.
Communicate with the child using the two pipes in the original process.
That's easy said bot not so trivially implemented (At least for me). I oddly managed to do so in one case, but when I tried to understand what I am doing with a simpler example, I fail. Here is my current problem:
I have two programs. The first just writes a incremented number every 100 ms to it's stdout:
#include <unistd.h>
#include <time.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
void sleepMs(uint32_t ms) {
struct timespec ts;
ts.tv_sec = 0 + (ms / 1000);
ts.tv_nsec = 1000 * 1000 * (ms % 1000);
nanosleep(&ts, NULL);
}
int main(int argc, char *argv[]) {
long int cnt = 0;
char buf[0x10] = {0};
while (1) {
sleepMs(100);
sprintf(buf, "%ld\n", ++cnt);
if (write(STDOUT_FILENO, buf, strlen(buf)) == -1)
perror("write");
}
}
Now the second program is supposed to read the stdout of the first program (Please keep in my mind that I eventually want to read AND write with a process, so a technical correct solution to use popen() for the upper use case might be right in this specific case, because I simplified my experiments to just capture the stdout of the bottom program). I expect from the bottom program that it reads whatever data the upper program writes to stdout. But it does not read anything. Where could be the reason? (second question).
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
void sleepMs(uint32_t ms) {
struct timespec ts;
ts.tv_sec = 0 + (ms / 1000);
ts.tv_nsec = 1000 * 1000 * (ms % 1000);
nanosleep(&ts, NULL);
}
int main() {
int pipe_fds[2];
int n;
char buf[0x100] = {0};
pid_t pid;
pipe(pipe_fds);
char *cmd[] = {"/path/to/program/above", NULL};
if ((pid = fork()) == 0) { /* child */
dup2(pipe_fds[1], 1); // set stdout of the process to the write end of the pipe
execvp(cmd[0], cmd); // execute the program.
fflush(stdout);
perror(cmd[0]); // only reached in case of error
exit(0);
} else if (pid == -1) { /* failed */
perror("fork");
exit(1);
} else { /* parent */
while (1) {
sleepMs(500); // Wait a bit to let the child program run a little
printf("Trying to read\n");
if ((n = read(pipe_fds[0], buf, 0x100)) >= 0) { // Try to read stdout of the child process from the read end of the pipe
buf[n] = 0; /* terminate the string */
fprintf(stderr, "Got: %s", buf); // this should print "1 2 3 4 5 6 7 8 9 10 ..."
} else {
fprintf(stderr, "read failed\n");
perror("read");
}
}
}
}
Here is a (C++11-flavored) complete example:
//
// Example of communication with a subprocess via stdin/stdout
// Author: Konstantin Tretyakov
// License: MIT
//
#include <ext/stdio_filebuf.h> // NB: Specific to libstdc++
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>
#include <memory>
#include <exception>
// Wrapping pipe in a class makes sure they are closed when we leave scope
class cpipe {
private:
int fd[2];
public:
const inline int read_fd() const { return fd[0]; }
const inline int write_fd() const { return fd[1]; }
cpipe() { if (pipe(fd)) throw std::runtime_error("Failed to create pipe"); }
void close() { ::close(fd[0]); ::close(fd[1]); }
~cpipe() { close(); }
};
//
// Usage:
// spawn s(argv)
// s.stdin << ...
// s.stdout >> ...
// s.send_eol()
// s.wait()
//
class spawn {
private:
cpipe write_pipe;
cpipe read_pipe;
public:
int child_pid = -1;
std::unique_ptr<__gnu_cxx::stdio_filebuf<char> > write_buf = NULL;
std::unique_ptr<__gnu_cxx::stdio_filebuf<char> > read_buf = NULL;
std::ostream stdin;
std::istream stdout;
spawn(const char* const argv[], bool with_path = false, const char* const envp[] = 0): stdin(NULL), stdout(NULL) {
child_pid = fork();
if (child_pid == -1) throw std::runtime_error("Failed to start child process");
if (child_pid == 0) { // In child process
dup2(write_pipe.read_fd(), STDIN_FILENO);
dup2(read_pipe.write_fd(), STDOUT_FILENO);
write_pipe.close(); read_pipe.close();
int result;
if (with_path) {
if (envp != 0) result = execvpe(argv[0], const_cast<char* const*>(argv), const_cast<char* const*>(envp));
else result = execvp(argv[0], const_cast<char* const*>(argv));
}
else {
if (envp != 0) result = execve(argv[0], const_cast<char* const*>(argv), const_cast<char* const*>(envp));
else result = execv(argv[0], const_cast<char* const*>(argv));
}
if (result == -1) {
// Note: no point writing to stdout here, it has been redirected
std::cerr << "Error: Failed to launch program" << std::endl;
exit(1);
}
}
else {
close(write_pipe.read_fd());
close(read_pipe.write_fd());
write_buf = std::unique_ptr<__gnu_cxx::stdio_filebuf<char> >(new __gnu_cxx::stdio_filebuf<char>(write_pipe.write_fd(), std::ios::out));
read_buf = std::unique_ptr<__gnu_cxx::stdio_filebuf<char> >(new __gnu_cxx::stdio_filebuf<char>(read_pipe.read_fd(), std::ios::in));
stdin.rdbuf(write_buf.get());
stdout.rdbuf(read_buf.get());
}
}
void send_eof() { write_buf->close(); }
int wait() {
int status;
waitpid(child_pid, &status, 0);
return status;
}
};
// ---------------- Usage example -------------------- //
#include <string>
using std::string;
using std::getline;
using std::cout;
using std::endl;
int main() {
const char* const argv[] = {"/bin/cat", (const char*)0};
spawn cat(argv);
cat.stdin << "Hello" << std::endl;
string s;
getline(cat.stdout, s);
cout << "Read from program: '" << s << "'" << endl;
cat.send_eof();
cout << "Waiting to terminate..." << endl;
cout << "Status: " << cat.wait() << endl;
return 0;
}
For many practical purposes, however, the Expect library could probably be a good choice (check out the code in the example subdirectory of its source distribution).
You've got the right idea, and I don't have time to analyze all of your code to point out the specific problem, but I do want to point out a few things that you may have overlooked on how programs and terminals work.
The idea of a terminal as a "file" is naivé. Programs like vi use a library (ncurses) to send special control characters (and change terminal device driver settings). For example, vi puts the terminal device driver itself into a mode where it can read a character at a time, among other things.
It is very non-trivial to "control" a program like vi this way.
On your simplified experiment...
Your buffer is one byte too small. Also, be aware IO is sometimes line buffered. So, you might try making sure the newline is getting transferred (use printf instead of sprintf/strlen/write...you hooked the stdout up to your pipe already), otherwise you might not see data until a newline is hit. I don't remember pipe being line buffered, but it is worth a shot.

Unix shell: how do I check user input to see if it's a valid unix command?

I have an assignment in which I need to create a unix shell using fork(). I've got this working correctly. Now I need to check user input to see if it is a valid unix command. If it is not valid (ie its "1035813") I need to tell the user to enter a valid command.
Is there a way I can get a list of every possible unix command so I can compare the user input with every string in this list? Or is there an easier way to do this?
The appropriate way to do this is:
check if it's a built-in command in your shell. For example, cd should probably be a built in command.
fork and try to exec it. (execvp is probably what you really want, actually). If that fails, check errno to determine why.
Example:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char* argv[])
{
if (argc != 2) {
printf("usage: %s <program-to-run>\n", argv[0]);
return -1;
}
char* program = argv[1];
/* in this case we aren't passing any arguments to the program */
char* const args[] = { program, NULL };
printf("trying to run %s...\n", program);
pid_t pid = fork();
if (pid == -1) {
perror("failed to fork");
return -1;
}
if (pid == 0) {
/* child */
if (execvp(program, args) == -1) {
/* here errno is set. You can retrieve a message with either
* perror() or strerror()
*/
perror(program);
return -1;
}
} else {
/* parent */
int status;
waitpid(pid, &status, 0);
printf("%s exited with status %d\n", program, WEXITSTATUS(status));
}
}
You could check the output of which. If it doesn't start with which: no <1035813> in blah/blah then it's probably not a command on that system.
Try that.
if which $COMMAND
then echo "Valid Unix Command"
else
echo "Non valid Unix Command"
fi
If you'd like to find out, whether it is a builtin command, you could abuse help:
if help $COMMAND >/dev/null || which $COMMAND >/dev/null
then echo "Valid Unix Command"
else
echo "Not a valid command"
fi

Resources