How to execute the bash's sudo command using execvp function? - c++

I have an executable, a.out which takes several command-line arguments. This executable needs to be run with root privileges, like sudo ./a.out arg1 arg2 ... .
How should I do this using the execvp system call in C++ ?

char *args[5];
args[0]="sudo";
args[1]="./a.out";
args[2]="arg1";
args[3]="arg2";
args[4]=NULL;
execvp("sudo", args);
The shell splits the given command into whitespace-delimited words. The first word gets looked up in PATH, and all the words are passed as parameters to the command.
Sinec execvp() will search the PATH for you, you can simply pass "sudo" as the command, then the arguments to the command. Note that the first argument to the command is the command's name.
The command you're executing is "sudo", so that's the first argument; then the remaining words as individual arguments, passed as an array.

This is not working: ?
execvp("/usr/bin/sudo", "./a.out arg1 erg2").

Related

system(): Spaces in strings [duplicate]

How do I use the system() function in c++ if I want to enter a command that needs a path when my path has spaces in it?
Example code:
#include <iostream>
int main()
{
std::string command = "ls -la /home/testuser/this is a folder/test/";
std::cout << "Click enter to execute command..." << std::endl;
getchar();
std::system(command.c_str());
return 0;
}
This doesn't work supposedly because the shell needs backspaces in front of a space.
Unfortunately this doesn't work too:
std::string command = "ls -la /home/testuser/this\b is\b a\b folder/test/";
Any idea what I'm doing wrong or how I could do it better? Thanks.
Ask yourself: how would you execute this command, yourself, from a shell prompt?
$ ls -la /home/testuser/this is a folder/test/
This, of course, will not work for the same reason your program fails. Instead, as every primer on shell scripting teaches you, you need to quote the parameter:
$ ls -la "/home/testuser/this is a folder/test/"
That will work, and you use system() in exactly the same way:
std::string command = "ls -la \"/home/testuser/this is a folder/test/\"";
But what's even better is not using system() in the first place. All the system() is, for all practical purposes, is a fork(), followed by exec() in the child process, with the parent process wait()ing for the child process's termination.
The problem is that the child process exec() the system shell, which parses the command according to its rules. This includes all the normal things that occur when executing the command via the shell directly: filename expansion, globbing, and other things.
If the string that gets passed to exec() includes any special shell characters, they'll be interpreted by the shell. In this case, you're intentionally using this to correctly parse the command string, in order to pass the correct arguments to /bin/ls.
When executing a specific, fixed command, this is fine. But when the actual command varies, or contains externally-specified parameters, it is your responsibility to correctly handle any shell wildcard characters in order to get your intended result. Hillarity ensues, otherwise. In that situation, you will find that using fork() and exec() yourself will produce far more deterministic, and reliable results, where you are in complete control of all arguments that get passed to the command being executed, instead of relying on the system shell to do it for you.

C++ system() command path with spaces

How do I use the system() function in c++ if I want to enter a command that needs a path when my path has spaces in it?
Example code:
#include <iostream>
int main()
{
std::string command = "ls -la /home/testuser/this is a folder/test/";
std::cout << "Click enter to execute command..." << std::endl;
getchar();
std::system(command.c_str());
return 0;
}
This doesn't work supposedly because the shell needs backspaces in front of a space.
Unfortunately this doesn't work too:
std::string command = "ls -la /home/testuser/this\b is\b a\b folder/test/";
Any idea what I'm doing wrong or how I could do it better? Thanks.
Ask yourself: how would you execute this command, yourself, from a shell prompt?
$ ls -la /home/testuser/this is a folder/test/
This, of course, will not work for the same reason your program fails. Instead, as every primer on shell scripting teaches you, you need to quote the parameter:
$ ls -la "/home/testuser/this is a folder/test/"
That will work, and you use system() in exactly the same way:
std::string command = "ls -la \"/home/testuser/this is a folder/test/\"";
But what's even better is not using system() in the first place. All the system() is, for all practical purposes, is a fork(), followed by exec() in the child process, with the parent process wait()ing for the child process's termination.
The problem is that the child process exec() the system shell, which parses the command according to its rules. This includes all the normal things that occur when executing the command via the shell directly: filename expansion, globbing, and other things.
If the string that gets passed to exec() includes any special shell characters, they'll be interpreted by the shell. In this case, you're intentionally using this to correctly parse the command string, in order to pass the correct arguments to /bin/ls.
When executing a specific, fixed command, this is fine. But when the actual command varies, or contains externally-specified parameters, it is your responsibility to correctly handle any shell wildcard characters in order to get your intended result. Hillarity ensues, otherwise. In that situation, you will find that using fork() and exec() yourself will produce far more deterministic, and reliable results, where you are in complete control of all arguments that get passed to the command being executed, instead of relying on the system shell to do it for you.

How to pass arguments when calling `source` command

I need to run source command from c++ program and pass filename and also some arguments. Is it possible? I want to use them in script like command line arguments (with argc, argv0, ...). http://www.astro.princeton.edu/~rhl/Tcl-Tk_docs/tcl/source.n.html here is not specified how to do it.
When doing this from C or C++, you should:
Initialise the Tcl library and create a Tcl interpreter.
Set the global variables argv0, argv and argc to the values expected by a normal Tcl script. This is exactly what tclsh does; the variables are entirely ordinary apart from being initialised this way.
argv0 is the name of the “main” script, which might be the script you're about to source.
argv is a Tcl list of all the other arguments; argc is the length of that list.
Use Tcl_FSEvalFileEx(interp,pathPtr,encoding) to execute the file; the source command is a very thin wrapper around that call. You probably want to pass the encoding argument as NULL, and the pathPtr argument is a Tcl_Obj reference.
If your script accepts the arguments in argv, just set this variable before you source this script.
But if this script calls exit, it will terminate the entire process, usually not what you want. You could use a slave interp to avoid this.
There are 3 predefined variables:
$argc - number items of arguments passed to a script.
$argv - list of the arguments.
$argv0 - name of the script.
So in your case, assuming the file sourced is in the same directory and its name is passed as a first argument:
source [lindex $argv 0]

Strange behviour while parsing command line arguments c++

here is an issue I have never seen before and thought it would be share-worthy. Im not sure why it happens though.
This is how I invoke my program:
./foo -switch1 arg1 -switch2 arg2 -switch3 arg3|arg4|arg5 -switch4 -arg6
Each switch is used to indicate a different type of argument and I parse them accordingly.
The problem occurs with switch3, which indicates that arg3, arg4, arg5 all correspond to the same switch and are delineated using the | character.
For some reason, I can run the program perfectly, but when I try to debug it using gdb, My program crashes with a during startup, program exited with code 127 error.
Here's what intrigues me. It also says bin/bash: arg4 not found. It takes the argument right after the first | character, which now I assume it perceives as the pipe character, and tries to invoke a bash script.
Why does this happen??? Doesn't the compiler take the entire command line string and consider space separated tokens as different arguments? Why is the | being interpreted differently? I tried adding arg3|arg4|arg5 in inverted quotes "", and it works fine. I also tried separating them by -, eg. arg3-arg4-arg5, and this works fine too.
The | character has a special meaning in bash: it creates a pipeline.
Your program only sees the following arguments:
./foo -switch1 arg1 -switch2 arg2 -switch3 arg3
The |arg4 syntax is interpreted by bash to mean that the (non-existent) arg4 command should be run, and that the standard output of ./foo should be piped into the standard input of arg4. The |arg5 ... is interpreted in the same manner.
To suppress this behaviour, run your program like so:
./foo -switch1 arg1 -switch2 arg2 -switch3 'arg3|arg4|arg5' -switch4 -arg6
(note the quotes).
The problem is that | is a pipe so you end up trying to call arg4 which the shell can not find. You need to quote the content in with pipes in them i.e. "arg3|arg4|arg5":
./foo -switch1 arg1 -switch2 arg2 -switch3 "arg3|arg4|arg5" -switch4 -arg6
If you are curious you can check out bash pitfalls. If you are not doing a lot of shell programming you may not see these problems often but when you do it may take you a while to figure out what is going on so learning more about shell programming can be helpful in the long term. I remember hitting number 3 and it took me a while to find a good solution the first time.

gengetopt: How to parse a string without an option (like a file name)

I'm trying to parse command line options using code generated by gengetopt, and I'm trying to figure out how to parse an extra argument (after all the other options) that has no long or short option.
I'd like to call it like this:
program [options] [input file]
ex:
program -a -b -letterc "C:\somefile.txt"
or
program -a -b -letterc somefile.txt
where the option "letterc" has no arguments.
Does anyone know how to do this using gengetopt?
These should be avaiable in the inputs member of the gengetopt_args_info. There are input_num of them.