I'd like to write a function that generate gz file. The function will only be operational on Linux so I'd like to use gzip command (just execute external command).
So far I have this:
bool generate_gz( const String& path )
{
bool res = false;
// LINUX
#ifndef __WXMSW__
if( !gzip_command_exists())
cout << "cannot compress file. 'gzip' command is not available.\n";
else
res = (0 == execute_command(String::Format("gzip %s", path.c_str())));
// WINDOWS
#else
// do nothing - result will be false
#endif
return res;
}
bool gzip_command_exists()
{
// TBD
}
Question
Is there a way to implement gzip_command_exists()? If so, does it have to involve running ( or trying to run) gzip command?
The simplest is to execute via system() : "which gzip" and see the exit code of the system call:
RETURN VALUE
The value returned is -1 on error (e.g. fork(2) failed), and the return status of the command otherwise. This latter return status
is in
the format specified in wait(2). Thus, the exit code of the command will be WEXITSTATUS(status). In case /bin/sh could not be
executed,
the exit status will be that of a command that does exit(127).
What to look for:
:~$ which gzip
/bin/gzip
:~$ echo $?
0
:~$ which gzip11
:~$ echo $?
1
If you do not want to spawn an external command, you can use the stat function to check if a file exists and if it is executable on a POSIX system.
It you do not want to hard code the path to gzip it is slightly more complicated. You will have to obtain the PATH environment variable, split it on colons, and then check each path for gzip. Again the name and format of path variables are POSIX specific. Check getenv function to read the path, and you could use strtok to split it.
It is questionable if it is worth checking, though, vs. just trying to run it and handling any errors.
You could use popen(3) to read the output of /usr/bin/which gzip (and you could also use it to compress on the fly by write-popen-ing a gzip > file.gz command). you could also have: FILE* pgzipv = popen("gzip --version", "r"); and fgets the first line then pclose....
You could consider using getenv("PATH") then making a loop on it with an access test to each constructed path obtained by appending /gzip to each element in the PATH, etc... You could also fork then execvp using gzip --version with stdout and stderr suitably redirected, etc..
Notice that both popen(3) and system(3) would fail when asked to execute a non-existing program (since they both fork(2) a /bin/sh shell with -c). So you don't need to test the existence of gzip and you always need to test the success of system or popen (which can fail for many reasons, see below for fork failure, and the documentation for other failures).
To be picky, checking that gzip exists is useless: it [the file /bin/gzip] could (unlikely) have been removed between your check -e.g. with access as below or with popen as above- and your later invocation of system or popen; so your first check for gzip don't bring anything.
On most Linux systems, gzip is generally available at /bin/gzip (and in practice gzip is always installed);
this is required by the file system hierarchy standard (which says that if gzip is installed it should be at that file path). Then you could just use access(2) e.g. with code like
#define GZIP_PATH "/bin/gzip" /* per FSH, see www.pathname.com/fhs */
if (access(GZIP_PATH, X_OK)) { perror(GZIP_PATH); exit(EXIT_FAILURE); };
At last, you don't need at all to fork a gzip process to gzip-compress a file. You could (and you should) simply use a library like zlib (which is required according to the Linux Standard Base as libz.so.1); you want its gzopen, gzwrite, gzprintf, gzputs, gzclose etc .... functions! That would be faster (no need to fork(2) any external process) and more reliable (no dependency on some external program like gzip; would work even if fork is not possible because limits have been reached - see setrlimit(2) with RLIMIT_NPROC and ulimit builtin of bash(1))
See also Advanced Linux Programming
Related
I run unzip via a system() call in my C++ code in below format:
/usr/bin/unzip -o -q /<my_path_to_zip_file>/cfg_T-KTMAKUCB.zip -d /<my_path_to_dest>/../
This will almost 90% of times succeed. I cannot understand what could make it fail time to time with -1 return code. Any ideas?
According my local man system,
The value returned is -1 on error (e.g. fork(2) failed), and the return status of the command otherwise.
and the POSIX spec says,
If a child process cannot be created, or if the termination status for the command language interpreter cannot be obtained, system() shall return -1 and set errno to indicate the error
Finally, the manpage for unzip lists various return codes, but -1 isn't among them.
If the command itself can't return -1, the problem is probably with the initial fork/exec, due to something like a system-wide or per-user limit (memory exhausted; process table full; maximum processes, open files or VM size limit for the user etc. etc).
You should be checking errno when system fails anyway. Running the whole thing under strace -f will also show what happens.
I wrote a c++ program to check if a process is running or not . this process is independently launched at background . my program works fine when I run it on foreground but when I time schedule it, it do not work .
int PID= ReadCommanOutput("pidof /root/test/testProg1"); /// also tested with pidof -m
I made a script in /etc/cron.d/myscript to time schedule it as follows :-
45 15 * * * root /root/ProgramMonitor/./testBkg > /root/ProgramMonitor/OutPut.txt
what could be the reason for this ?
string ReadCommanOutput(string command)
{
string output="";
int its=system((command+" > /root/ProgramMonitor/macinfo.txt").c_str());
if(its==0)
{
ifstream reader1("/root/ProgramMonitor/macinfo.txt",fstream::in);
if(!reader1.fail())
{
while(!reader1.eof())
{
string line;
getline(reader1,line);
if(reader1.fail())// for last read
break;
if(!line.empty())
{
stringstream ss(line.c_str());
ss>>output;
cout<<command<<" output = ["<<output<<"]"<<endl;
break;
}
}
reader1.close();
remove("/root/ProgramMonitor/macinfo.txt");
}
else
cout<<"/root/ProgramMonitor/macinfo.txt not found !"<<endl;
}
else
cout<<"ERROR: code = "<<its<<endl;
return output;
}
its output coming as "ERROR: code = 256"
thanks in advacee .
If you really wanted to pipe(2), fork(2), execve(2) then read the output of a pidof command, you should at least use popen(3) since ReadCommandOutput is not in the Posix API; at the very least
pid_t thepid = 0;
FILE* fpidof = popen("pidof /root/test/testProg1");
if (fpidof) {
int p=0;
if (fscanf(fpidof, "%d", &p)>0 && p>0)
thepid = (pid_t)p;
pclose(fpidof);
}
BTW, you did not specify what should happen if several processes (or none) are running the testProg1....; you also need to check the result of pclose
But you don't need to; actually you'll want to build, perhaps using snprintf, the pidof command (and you should be scared of code injection into that command, so quote arguments appropriately). You could simply find your command by accessing the proc(5) file system: you would opendir(3) on "/proc/", then loop on readdir(3) and for every entry which has a numerical name like 1234 (starts with a digit) readlink(2) its exe entry like e.g. /proc/1234/exe ...). Don't forget the closedir and test every syscall.
Please read Advanced Linux Programming
Notice that libraries like Poco or toolkits like Qt (which has a layer QCore without any GUI, and providing QProcess ....) could be useful to you.
As to why your pidof is failing, we can't guess (perhaps a permission issue, or perhaps there is no more any process like you want). Try to run it as root in another terminal at least. Test its exit code, and display both its stdout & stderr at least for debugging purposes.
Also, a better way (assuming that testProg1 is some kind of a server application, to be run in at most one single process) might be to define different conventions. Your testProg1 might start by writing its own pid into /var/run/testProg1.pid and your current application might then read the pid from that file and check, with kill(2) and a 0 signal number, that the process is still existing.
BTW, you could also improve your crontab(5) entry. You could make it run some shell script which uses logger(1) and (for debugging) runs pidof with its output redirected elsewhere. You might also read the mail perhaps sent to root by cron.
Finally I solved this problem by using su command
I have used
ReadCommanOutput("su -c 'pidof /root/test/testProg1' - root");
insteadof
ReadCommanOutput("pidof /root/test/testProg1");
Read some where that one should use WEXITSTATUS to check for return status of system calls .
However I dont think that a call like system("mv /a/b/c /a/b/d") needs a check if it fails .
What are the conditions when this call may fail ?
Some possibilities:
/a/b/c does not exist
/a/b does not exist
You have insufficient access to /a/b/c
You have insufficient access to /a/b/d
/a/b/d already exists
/a/b/c isn't moveable
There is no shell
mv does not exist
You have insufficient access to mv
You have no file system mounted
You have no storage available at all
And many, many more...
system("mv /a/b/c /a/b/d") : very probably both /a/b/c and /a/b/d lie on the same mounted file system. I am guessing you have a Posix system, perhaps Linux.
Then it is much more simpler to use the rename(2) syscall (which is called by /bin/mv when relevant!) and directly code:
if (rename("/a/b/c", "/a/b/d")) {
perror("rename failed");
exit(EXIT_FAILURE);
}
and you would have thru errno(3) i.e. thru perror(3) the error code explaining why the rename failed. All the error conditions in rename(2) are relevant failure cases of mv, see also mv(1)!
Read Advanced Linux Programming.
If (for some strange reason, e.g. bind mounts, symlinks, ...) /a/b/c and /a/b/d don't lie in the same filesystem, you would get errno == EXDEV (and you might handle that case by a copy followed by an unlink of the source).
In general, using system("mv ...") should be avoided. See this answer and this one explaining why (and also this and this). And your user may have a different mv in his PATH (or some alias), so mv should at least be /bin/mv ... If . (or even worse /tmp !) is early in his PATH and has a symlink mv ➙ /bin/rm your user won't be happy!
BTW, you generally don't call system with a compile-time constant string starting with mv. That string is generally built. And if you don't quote correctly things (imagine one argument being ; rm -rf $HOME) havoc can happen.
Also, system(3) can fail e.g. because fork(2) failed (too many user processes, reaching the RLIMIT_NPROC limit of setrlimit(2)...).
I am having trouble using system() from libc on Linux. My code is this:
system( "tar zxvOf some.tar.gz fileToExtract | sed 's/some text to remove//' > output" );
std::string line;
int count = 0;
std::ifstream inputFile( "output" );
while( std::getline( input, line != NULL ) )
++count;
I run this snippet repeatedly and occasionally I find that count == 0 at the end of the run - no lines have been read from the file. I look at the file system and the file has the contents I would expect (greater than zero lines).
My question is should system() return when the entire command passed in has completed or does the presence of the pipe '|' mean system() can return before the part of the command after the pipe is completed?
I have explicitly not used a '&' to background any part of the command to system().
To further clarify I do in practice run the code snippet multiples times in parallel but the output file is a unique filename named after the thread ID and a static integer incremented per call to system(). I'm confident that the file being output to and read is unique for each call to system().
According to the documentation
The system() function shall not return until the child process has terminated.
Perhaps capture the output of "output" when it fails and see what it is? In addition, checking the return value of system would be a good idea. One scenario is that the shell command you are running is failing and you aren't checking the return value.
system(...) calls the standard shell to execute the command, and the shell itself should return only after the shell has regained control over the terminal. So if there's one of the programs backgrounded, system will return early.
Backgrounding happens through suffixing a command with & so check if the string you pass to system(...) contains any & and if so make sure they're properly quoted from shell processing.
System will only return after completion of its command and the file output should be readable in full after that. But ...
... multiple instances of your code snippet run in parallel would interfere because all use the same file output. If you just want to examine the contents of output and do not need the file itself, I would use popen instead of system. popen allows you to read the output of the pipe via a FILE*.
In case of a full file system, you could also see an empty output while the popen version would have no trouble with this condition.
To notice errors like a full file system, always check the return code of your calls (system, popen, ...). If there is an error the manpage will tell you to check errno. The number errno can be converted to a human readable text by strerror and output by perror.
On a Linux platform, I have C++ code that goes like this:
// ...
std::string myDir;
myDir = argv[1]; // myDir is initialized using user input from the command line.
std::string command;
command = "mkdir " + myDir;
if (system(command.c_str()) != 0) {
return 1;
}
// continue....
Is passing user input to a system() call safe at all?
Should the user input be escaped / sanitized?
How?
How could the above code be exploited for malicious purposes?
Thanks.
Just don't use system. Prefer execl.
execl ("/bin/mkdir", "mkdir", myDir, (char *)0);
That way, myDir is always passed as a single argument to mkdir, and the shell isn't involved. Note that you need to fork if you use this method.
But if this is not just an example, you should use the mkdir C function:
mkdir(myDir, someMode);
Using system() call with command line parameters without sanitizing the input can be highly insecure.
The potential security threat could be a user passing the following as directory name
somedir ; rm -rf /
To prevent this , use a mixture of the following
use getopt to ensure your input is
sanitized
sanitize the input
use execl instead of system to execute
the command
The best option would be to use all three
Further to Matthew's answer, don't spawn a shell process unless you absolutely need it. If you use a fork/execl combination, individual parameters will never be parsed so don't need to be escaped. Beware of null characters however which will still prematurely terminate the parameter (this is not a security problem in some cases).
I assume mkdir is just an example, as mkdir can trivially be called from C++ much more easily than these subprocess suggestions.
Reviving this ancient question as I ran into the same problem and the top answers, based on fork() + execl(), weren't working for me. (They create a separate process, whereas I wanted to use async to launch the command in a thread and have the system call stay in-process to share state more easily.) So I'll give an alternative solution.
It's not usually safe to pass user input as-is, especially if the utility is designed to be sudo'd; in order to sanitize it, instead of composing the string to be executed yourself, use environment variables, which the shell has built-in escape mechanisms for.
For your example:
// ...
std::string myDir;
myDir = argv[1]; // myDir is initialized using user input from the command line.
setenv("MY_DIR", myDir, 1);
if (system("mkdir \"${MY_DIR}\"") != 0) {
return 1;
}
// continue....