Why does execl() fail with errno ENIVAL? - c

I was trying to write a simple program in C, to extract the files of a zip using 7zip. I tried the exec() family of functions to start 7zip but it fails with errno=22 [ENIVAL] - Invalid Arguments.
if(-1 == execl(
"\"C:\\Program Files\\7-Zip\\7z.exe\"",
"\"C:\\Program Files\\7-Zip\\7z.exe\"",
"x -y",
"myZip.7z",
NULL
)
) {
fprintf(stderr, "%s\n", strerror(errno));
}
I tried execlp(), execv(), execvp() etc., all of them fail with the same error. The only way it works is using it with system().
Running: "C:\\Program Files\\7-Zip\\7z.exe" x -y myZip.7z from the cmd works fine. It successfully extracts the contents.
Any suggestions on what I am doing wrong and how to fix this is much appreciated.

Per Microsoft's documentation:
Important
This API cannot be used in applications that execute in the Windows
Runtime. For more information, see CRT functions not supported in
Universal Windows Platform
apps.
Possible alternatives are the _[w]spawn*() family of calls:
Remarks
The _spawn functions each create and execute a new process. They
automatically handle multibyte-character string arguments as
appropriate, recognizing multibyte-character sequences according to
the multibyte code page currently in use. The _wspawn functions are
wide-character versions of the _spawn functions; they don't handle
multibyte-character strings. Otherwise, the _wspawn functions behave
identically to their _spawn counterparts.
Note that the _spawn*() family of functions tends not to handle arguments with spaces embedded in them very well - filenames with spaces may need to be passed with quote marks surrounding the filename.

Related

Using linux system() function C++ beagle bone issue [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have searched around but can't find an answer to my problem.
I am cross compiling a C++ application for the beagle bone black and wish to use the linux system() function as follows :
system("echo DM-GPIO-Test > $SLOTS");
It is to add a device overlay to control GPIO pins. The echo command "echo DM-GPIO-Test > $SLOTS" works fine when executed directly on the terminal on the beagle bone from anywhere. SLOTS is an environmental variable I defined and DM-GPIO-Test-00A0.dtb0 is in /lib/firmware
I get the following error on execution of the c++ application however:
"sh: 1: cannot create : Directory nonexistent"
Is it incorrect to call the system function like I did ?
Thanks in advance
system("echo DM-GPIO-Test > $SLOTS");
This smells bad and should be avoided.
What you probably want is to write a string in a file given by your SLOTS environment variable (see environ(7)). For that particular use, you don't need to fork any /bin/sh process (which is what system(3) does). You could simply fetch that environment variable using getenv(3).
So you might try:
const char*slotspath = getenv("SLOTS");
if (!slotspath) {
fprintf(stderr, "no SLOTS\n");
exit(EXIT_FAILURE);
}
FILE* fslots = fopen(slotspath, "w");
if (!fslots) { perror(slotspath); exit(EXIT_FAILURE); };
fputs("DM-GPIO-Test\n", fslots);
fclose(fslots), fslots = NULL;
Be aware that the environment of your program -assuming it is started by some other utility (or from init or systemd)- is probably different (and smaller) than your interactive environment.
Perhaps your slotspath should not come from your environment, but from some configuration file under /etc/ (that your program should parse), or some program argument.
So I suggest to define the format of some configuration file and parse it, and get your slotspath from it.
No, it doesn't display anything doing system("echo $SLOTS")
In this case the environment variable SLOTS is simply unset/empty in the environment used by your application. Depending on your use case you either need to set it before you start the binary, or using setenv(), or replacing it directly in the string you pass to system(). If you expect the variable to be set in any of the user's profile settings you need to be aware of the different behaviour of your shell (e. g. bash) at invocation, and place it in the right file, or create a wrapper script that sets it.
$ cat .profile
# ~/.profile: executed by the command interpreter for login shells.
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
[...]
A good idea would also be to check it for valid values in your application prior to the system() call, using getenv() to obtain it and asserting if it doesn't match the requirements.
Be aware that it is best practice to enclose shell variables containing strings into double quotes, in case they contain spaces, and the identifier into curly brackets, to avoid ambiguities:
system("echo DM-GPIO-Test > \"${SLOTS}\"");
And as Basile points out in a separate answer, avoiding invocation of the shell and handling the logic of writing the file completely in C would even be more powerful regarding the handling of errors and special cases, but also results in more code (which could contain bugs of its own...).

how to get status in c for system() commands

I want to get the status of command which i passed as a parameter to the system() function. suppose i pass "attrib +h E:/songs/lovingsong.mp3" as system("attrib +h E:/songs/lovingsong.mp3") since i know that the path give by me is not right , i.e there is no such folder songs in my E: drive so on console it says path not found.
How can i get "path not found" message in my c program or any status code ,so that i can know the command did not work.
/* system example : DIR */
#include <stdio.h> /* printf */
#include <stdlib.h> /* system, NULL, EXIT_FAILURE */
int main ()
{
int i;
printf ("Checking if processor is available...");
if (system(NULL)) puts ("Ok");
else exit (EXIT_FAILURE);
printf ("Executing command DIR...\n");
i = system ("attrib +h E:/songs/lovingsong.mp3");
//want to get status here
printf ("The value returned was: %d.\n",i);
return 0;
}
Notice that the C11 standard (read n1570) defines system in §7.22.4.8 (with its formal argument being called string below):
If string is a null pointer, the system function determines whether the host environment has a command processor. If string is not a null pointer, the system function passes the string pointed to by string to that command processor to be executed in a manner which the implementation shall document; this might then cause the program calling
system to behave in a non-conforming manner or to terminate
So there is no clear definition, according to the C11 standard, of what system does. You need to dive into the documentation of your particular C11 implementation.
On POSIX (e.g. Linux or MacOSX), system is required to run the POSIX shell /bin/sh with the -c argument followed by the string and there is a complex chapter describing what is a POSIX shell.
Perhaps you want to pass a command which is redirecting the stderr of attrib to stdout, and read that stdout produced by your attrib command. On POSIX you could use popen (with pclose) for that purpose. Maybe (and probably) your implementation has something similar: _popen. And you also need to read the documentation of your attrib command.
So your question is really operating system specific. You need to dive into the documentation of your particular operating system.
And you might simply test (before running system) that the file E:/songs/lovingsong.mp3 exists. Maybe use _access (with perhaps a race condition if that file gets removed before running attrib).
Be also aware of the $PATH variable.
PS. I don't know anything about Windows, but you did not tag your question with Windows.

How do you execute a bash script with "system()" when there are spaces in the file path?

I created a simple bash script called "myscript.h" I gave it a .h extensions for reasons that I won't disclose here. This bash script lives in "/var/ftp/something with spaces".
From the terminal, I can type in "/var/ftp/something with spaces/myscript.h" and the script works perfectly.
However, from within my C program, I type in
system("/var/ftp/something with spaces/myscript.h")
and it complains that "/var/ftp/something" is not found. I've changed my system call to the following with forward slashes:
system("/var/ftp/something\ with\ spaces/myscript.h")
However, it still complains that "/var/ftp/something" is not found. Assuming I can't change the directory names, how can I get around this?
Thanks!
To run a single script, you might avoid the system(3) library function (and use lower level system calls like fork(2), execve(2), waitpid(2)... which are used by the implementation of system(3)), or you could quote the script name when passing it to system(3).
For more details, read Advanced Linux Programming.
On Linux, system(3) is documented to fork a /bin/sh -c process. See sh(1p). And that POSIX shell has some quoting rules. You might use double-quotes and backslashes appropriately. So you would construct (and perhaps check) the string passed to system(3) (perhaps using asprintf(3) or snprintf(3) with care). Be aware that the C compiler also has (different) quoting conventions for string literals.
In general, you should avoid code injection (imagine a naughty user giving some a; rm -rf $HOME &; input as a "directory" name; you don't want to run system(3) on the weird "/var/ftp/a; rm -rf $HOME &;/myscript.h" string)
In your particular case, I recommend using fork(2), execve(2) (perhaps thru some carefully choosen exec(3) function), waitpid(2)... appropriately. This has the slight advantage to avoid depending on and running /bin/sh so could be slightly faster (by a millisecond).
Understand more the role of an Unix shell; for example, read about the various shell expansions in bash (they are similar to those mandated by POSIX sh) and be aware of globbing. See glob(7)
Note that you're adding quotes when running from the shell. You need to do the same here. Add quotes to the path name you're sending to system:
system("\"/var/ftp/something with spaces/myscript.h\"")
This should work with gcc version 5.4.0
system("\'\'/var/ftp/something\\ with\\ spaces/myscript.h\'\'");
Just put the filename inside single quotes
system("rm '/var/ftp/something with spaces/myscript.h'")

What does initialize_main (&argc, &argv) do?

I 'm reading coreutils source code to learn programming under linux.
I find that in most of the programs like ls.c, cat.c, they invoke the macro function initialize_main() at the first few lines. So I looked into system.h to find the implementation:
/* Redirection and wildcarding when done by the utility itself.
Generally a noop, but used in particular for native VMS. */
#ifndef initialize_main
# define initialize_main(ac, av)
#endif
I don't understand why define such a macro and I don't understand the comment.
The first step in understanding the comment is to know what VMS is. So here's a link for that:
http://en.wikipedia.org/wiki/OpenVMS
The next step is to understand redirection and wildcarding. In Linux and other members of the unix family, a command like
cat foo* > /tmp/foolist
will call the main function of cat with argv containing the matches for foo*. The output file /tmp/foolist will already be open as stdout before main is entered.
VMS doesn't do that. cat will find the unexpanded string "foo*" and the redirection operator > in its argv. So the utility itself (cat) must do the redirection (opening the output file) and wildcarding (replacing "foo*" with "foo1", "foo2", "foo3"). That's what initialize_main will do on VMS. On unix, it'll do nothing ("Generally a noop").
This is left over from times gone by. OpenVMS is an operating system which roughly competed with Unix in the past. There is still a fair amount of OpenVMS running in the world, but HP have dropped support for it and it will be going away in the next 10-15 years.
Anyway, this function is used on OpenVMS to allow stdout and stderr redirection on VMS.
Since cat foo.txt > stuff.txt on Unix, the cat command only sees one argument foo.txt, but on VMS, which knows nothing of the > symbol, the cat command sees 3 arguments.
The code inside initialize_main on VMS, allows the basic unix style commands to support output redirection, such as ls and
OpenVMS later added a command called pipe which allows redirection to work via any command.
You can view the source code for initialize_main on VMS here: Link

c console application auto-complete dynamic arguments

I am looking for a method of having console auto complete - such that given an application like:
int main (int argc, char ** argv)
{
if (argc == 1) return EXIT_FAILURE;
if (strcmp(argv[1], "good")==0) printf("good\n");
if (strcmp(argv[1], "bad")==0) printf("bad\n");
return EXIT_FAILURE;
}
When running it, I would like pressing [tab] after the command, such that it would give me one of the possible useful options.
Example:
./a.out g[tab]
would auto complete to
./a.out good
I don't want to edit /etc/bash-completion.d/, I was hoping for a much stronger auto-complete, something like a function in the executable itself that would be called - perhaps so it could query a database for the list of possible options. Or perhaps output a message letting you know what the options are.
If you think this is simply totally impossible, let me know!
Completions are a property of the shell you run the application from. You will have to provide completion functions for all the shells you want to support (bash, zsh, tcsh and fish have customizable completions). A completion function can call your application (e.g. run you_application --list-possible-arguments) or do whatever it chooses to generate the completions — it's already a “strong” completion in your terminology.
In bash, you declare completions with the complete built-in. Look in /etc/completion.d for examples (gpg is a fairly simple example; git is a rather involved one).
If you are using BASH then have a look at this similar post:
Auto-complete command line arguments
================================
If you want to provide your own command line then have a look at the Readline library:
The GNU Readline library provides a
set of functions for use by
applications that allow users to edit
command lines as they are typed in.
Both Emacs and vi editing modes are
available. The Readline library
includes additional functions to
maintain a list of previously-entered
command lines, to recall and perhaps
reedit those lines, and perform
csh-like history expansion on previous
commands.

Resources