I have an ARM-based device running Embedded Linux and I have observed that when I use the C library's system() call, the return code is incorrect. Here is a test program that demonstrates the behavior:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int ret = system("exit 42");
printf("Should return 42 for system() call: %d\n", ret);
printf("Returning 43 to shell..\n");
exit(43);
};
And here is the program output on the device:
# returnCodeTest
Should return 42 for system() call: 10752
Returning 43 to shell..
The value "10752" is returned by system() instead of "42". 10752 is 42 when left-shifted by 8:
Python 2.7.3 (default, Feb 27 2014, 20:00:17)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 42<<8
10752
So is suspect one of the following is going on somewhere:
The byte order is getting swapped
The value is getting shifted by 8 bits
Incompatible struct definitions are being used
When I run strace I see the following:
# strace /usr/bin/returnCodeTest
...
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x4001e308) = 977
wait4(977, [{WIFEXITED(s) && WEXITSTATUS(s) == 42}], 0, NULL) = 977
rt_sigaction(SIGINT, {SIG_DFL, [], 0x4000000 /* SA_??? */}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL, [], 0x4000000 /* SA_??? */}, NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=977, si_status=42, si_utime=0, si_stime=0} ---
fstat64(1, {st_mode=S_IFCHR|0622, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4001f000
write(1, "Should return 42 for system() ca"..., 42Should return 42 for system() call: 10752
) = 42
write(1, "Returning 43 to shell..\n", 24Returning 43 to shell..
) = 24
exit_group(43) = ?
+++ exited with 43 +++
wait4() returns with the correct status (si_status=42), but when it gets printed to standard output the value is shifted by 8 bits, and it looks like it is happening in a library. Interestingly the write returns a value of 42. I wonder if this is a hint as to what is going on...
Unfortunately I cannot get ltrace to compile and run on the device. Has anyone seen this type of behavior before or have any ideas (possibly architecture-specific) on where to look?
$man 3 system
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).
$man 2 wait
WEXITSTATUS(status) returns the exit status of the child. This
consists of the least significant 8 bits of the status argument that
the child specified in a call to exit(3) or _exit(2) or as the
argument for a return statement in main(). This macro should only be
employed if WIFEXITED returned true.
I think exit codes are different from return values and is specific to OS.
Linux does the following when you call "exit" with a code.
(error_code&0xff)<<8
SYSCALL_DEFINE1(exit, int, error_code)
{
do_exit((error_code&0xff)<<8);
}
Take look at the below link (exit status codes in Linux).
Are there any standard exit status codes in Linux?
Related
I am on Windows 10, having installed WinPython 64 3.8.5 and Mingw w64.
I want to call python code from C++ and started using the following example code:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
int main(int argc, char *argv[])
{
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
if (program == NULL) {
fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
exit(1);
}
Py_SetProgramName(program); /* optional but recommended */
Py_Initialize();
PyRun_SimpleString("from time import time,ctime\n"
"print('Today is', ctime(time()))\n");
if (Py_FinalizeEx() < 0) {
exit(120);
}
PyMem_RawFree(program);
return 0;
}
Compilation runs through smoothly (g++ myfile.cpp -Ipath/to/Python.h -Lpath/to/python38.lib -lpython38)
However, when running the exe in cmd (after running C:\Users\liebschs\Programs\WinPython64-3.8.5\WPy64-3850\scripts\env.batch in that cmd), I obtain the following error:
Python path configuration:
PYTHONHOME = (not set)
PYTHONPATH = (not set)
program name = 'embed.exe'
isolated = 0
environment = 1
user site = 1
import site = 1
sys._base_executable = 'C:\\Users\\liebschs\\MyFiles\\playground\\VSprojects\\EmbedPython\\embed.exe'
sys.base_prefix = 'C:\\Users\\liebschs\\Programs\\WinPython64-3.8.5\\WPy64-3850\\python-3.8.5.amd64'
sys.base_exec_prefix = 'C:\\Users\\liebschs\\Programs\\WinPython64-3.8.5\\WPy64-3850\\python-3.8.5.amd64'
sys.executable = 'C:\\Users\\liebschs\\MyFiles\\playground\\VSprojects\\EmbedPython\\embed.exe'
sys.prefix = 'C:\\Users\\liebschs\\Programs\\WinPython64-3.8.5\\WPy64-3850\\python-3.8.5.amd64'
sys.exec_prefix = 'C:\\Users\\liebschs\\Programs\\WinPython64-3.8.5\\WPy64-3850\\python-3.8.5.amd64'
sys.path = [
'C:\\Users\\liebschs\\Programs\\WinPython64-3.8.5\\WPy64-3850\\python-3.8.5.amd64\\python38.zip',
'.\\DLLs',
'.\\lib',
'C:\\Users\\liebschs\\MyFiles\\playground\\VSprojects\\EmbedPython',
]
Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
Python runtime state: core initialized
ModuleNotFoundError: No module named 'encodings'
Current thread 0x000021c8 (most recent call first):
<no Python frame>
Any help is appreciated!
Btw:
Running python scripts directly (i.e. doing "python helloworld.py" in cmd) or using pip does not cause an error and runs as expected.
switching to a virtual environment, does not change anything
Contrary to what I read elsewhere, the problem is resolved when I set PYTHONPATH and PYTHONHOME to the python.exe, in my case C:\Users\liebschs\Programs\WinPython64-3.8.5\WPy64-3850\python-3.8.5.amd64
I'm porting this piece of C code to VB.net. The function cat receives a pointer to a file, reads it and rewrites it into the console (works the same way as the TYPE command from DOS). My vb implementation works well for all types of files, except exe files, in which case, writes a lot more of "text" than what actually is in the exe file.
This is the C code:
enum {
BSIZE = 8192
};
int
cat(FILE *f)
{
char buf[BSIZE];
int n;
while((n = fread(buf, 1, BSIZE, f)) > 0)
if(fwrite(buf, 1, n, stdout) != n)
return 0;
return 1;
}
And this is the vb code:
Public Const BSIZE As Integer = 8192
Function cat(ByVal f As Stream) As Integer
Dim buf(BSIZE) As Byte
Dim n As Integer
Do
n = f.Read(buf, 0, BSIZE)
Try
Console.OpenStandardOutput.Write(buf, 0, n)
Catch ex As IOException
Return 0
End Try
Loop While n > 0
Return 1
End Function
For example, I used the C-exe of the program as the file to read. The C version writes about 10 lines of unintelligible characters, while the VB version writes more than 70; among these, I've noticed something interesting:
P§# Mingw runtime failure: VirtualQuery failed for %d bytes at
address %p Unknown pseudo relocation protocol version %d. Unknown
pseudo relocation bit size %d. GCC: (tdm-1) 5.1.0 GCC: (tdm-1)
5.1.0 GCC: (tdm-1) 5.1.0 GCC: (tdm-1) 5.1.0
It looks like the Stream.Read method is reading more than what it should read. Why is this happening?
The standard says that:
The perror() function shall not change the orientation of the standard error stream.
This is the implementation of perror() in GNU libc.
Following are the tests when stderr is wide-oriented, multibyte-oriented and not oriented, prior to calling perror().
Tests 1) and 2) are OK. The issue is in test 3).
1) stderr is wide-oriented:
#include <stdio.h>
#include <wchar.h>
#include <errno.h>
int main(void)
{
fwide(stderr, 1);
errno = EINVAL;
perror("");
int x = fwide(stderr, 0);
printf("fwide: %d\n",x);
return 0;
}
$ ./a.out
Invalid argument
fwide: 1
$ ./a.out 2>/dev/null
fwide: 1
2) stderr is multibyte-oriented:
#include <stdio.h>
#include <wchar.h>
#include <errno.h>
int main(void)
{
fwide(stderr, -1);
errno = EINVAL;
perror("");
int x = fwide(stderr, 0);
printf("fwide: %d\n",x);
return 0;
}
$ ./a.out
Invalid argument
fwide: -1
$ ./a.out 2>/dev/null
fwide: -1
3) stderr is not oriented:
#include <stdio.h>
#include <wchar.h>
#include <errno.h>
int main(void)
{
printf("initial fwide: %d\n", fwide(stderr, 0));
errno = EINVAL;
perror("");
int x = fwide(stderr, 0);
printf("fwide: %d\n", x);
return 0;
}
$ ./a.out
initial fwide: 0
Invalid argument
fwide: 0
$ ./a.out 2>/dev/null
initial fwide: 0
fwide: -1
Why perror() changes orientation of stream if it is redirected? Is it proper behavior?
How does this code work? What is this __dup trick all about?
TL;DR: Yes, it's a bug in glibc. If you care about it, you should report it.
The quoted requirement that perror not change the stream orientation is in Posix, but does not seem to be required by the C standard itself. However, Posix seems quite insistent that the orientation of stderr not be changed by perror, even if stderr is not yet oriented. XSH 2.5 Standard I/O Streams:
The perror(), psiginfo(), and psignal() functions shall behave as described above for the byte output functions if the stream is already byte-oriented, and shall behave as described above for the wide-character output functions if the stream is already wide-oriented. If the stream has no orientation, they shall behave as described for the byte output functions except that they shall not change the orientation of the stream.
And glibc attempts to implement Posix semantics. Unfortunately, it doesn't quite get it right.
Of course, it is impossible to write to a stream without setting its orientation. So in an attempt to comply with this curious requirement, glibc attempts to make a new stream based on the same fd as stderr, using the code pointed to at the end of the OP:
58 if (__builtin_expect (_IO_fwide (stderr, 0) != 0, 1)
59 || (fd = __fileno (stderr)) == -1
60 || (fd = __dup (fd)) == -1
61 || (fp = fdopen (fd, "w+")) == NULL)
62 { ...
which, stripping out the internal symbols, is essentially equivalent to:
if (fwide (stderr, 0) != 0
|| (fd = fileno (stderr)) == -1
|| (fd = dup (fd)) == -1
|| (fp = fdopen (fd, "w+")) == NULL)
{
/* Either stderr has an orientation or the duplication failed,
* so just write to stderr
*/
if (fd != -1) close(fd);
perror_internal(stderr, s, errnum);
}
else
{
/* Write the message to fp instead of stderr */
perror_internal(fp, s, errnum);
fclose(fp);
}
fileno extracts the fd from a standard C library stream. dup takes an fd, duplicates it, and returns the number of the copy. And fdopen creates a standard C library stream from an fd. In short, that doesn't reopen stderr; rather, it creates (or attempts to create) a copy of stderr which can be written to without affecting the orientation of stderr.
Unfortunately, it doesn't work reliably because of the mode:
fp = fdopen(fd, "w+");
That attempts to open a stream which allows both reading and writing. And it will work with the original stderr, which is just a copy of the console fd, originally opened for both reading and writing. But when you bind stderr to some other device with a redirect:
$ ./a.out 2>/dev/null
you are passing the executable an fd opened only for output. And fdopen won't let you get away with that:
The application shall ensure that the mode of the stream as expressed by the mode argument is allowed by the file access mode of the open file description to which fildes refers.
The glibc implementation of fdopen actually checks, and returns NULL with errno set to EINVAL if you specify a mode which requires access rights not available to the fd.
So you could get your test to pass if you redirect stderr for both reading and writing:
$ ./a.out 2<>/dev/null
But what you probably wanted in the first place was to redirect stderr in append mode:
$ ./a.out 2>>/dev/null
and as far as I know, bash does not provide a way to read/append redirect.
I don't know why the glibc code uses "w+" as a mode argument, since it has no intention of reading from stderr. "w" should work fine, although it probably won't preserve append mode, which might have unfortunate consequences.
I'm not sure if there's a good answer to "why" without asking the glibc developers - it may just be a bug - but the POSIX requirement seems to conflict with ISO C, which reads in 7.21.2, ¶4:
Each stream has an orientation. After a stream is associated with an external file, but before any operations are performed on it, the stream is without orientation. Once a wide character input/output function has been applied to a stream without orientation, the stream becomes a wide-oriented stream. Similarly, once a byte input/output function has been applied to a stream without orientation, the stream becomes a byte-oriented stream. Only a call to the freopen function or the fwide function can otherwise alter the orientation of a stream. (A successful call to freopen removes any orientation.)
Further, perror seems to qualify as a "byte I/O function" since it takes a char * and, per 7.21.10.4 ¶2, "writes a sequence of characters".
Since POSIX defers to ISO C in the event of a conflict, there is an argument to be made that the POSIX requirement here is void.
As for the actual examples in the question:
Undefined behavior. A byte I/O function is called on a wide-oriented stream.
Nothing at all controversial. The orientation was correct for calling perror and did not change as a result of the call.
Calling perror oriented the stream to byte orientation. This seems to be required by ISO C but disallowed by POSIX.
When I input gcc -v, it just shows the same...
I have try it in Debian and it behaves normally..
uname -i is non-portable according to man uname and it can be compiled out. My new Debian 8 also prints it as
$ uname -i
unknown
uname is part of the GNU coreutils and it is a very simple program. you can see the code on savannah. If you look through the command line option there --hardware-platform is -i:
88 static struct option const uname_long_options[] =
89 {
90 {"all", no_argument, NULL, 'a'},
...
97 {"machine", no_argument, NULL, 'm'},
98 {"processor", no_argument, NULL, 'p'},
99 {"hardware-platform", no_argument, NULL, 'i'},
100 {"operating-system", no_argument, NULL, 'o'},
101 {GETOPT_HELP_OPTION_DECL},
102 {GETOPT_VERSION_OPTION_DECL},
103 {NULL, 0, NULL, 0}
104 };
Which forces the printing of the define PRINT_HARDWARE_PLATFORM
198 while ((c = getopt_long (argc, argv, "asnrvmpio",
199 uname_long_options, NULL)) != -1)
200 {
201 switch (c)
202 {
...
227 case 'p':
228 toprint |= PRINT_PROCESSOR;
229 break;
230
231 case 'i':
232 toprint |= PRINT_HARDWARE_PLATFORM;
233 break;
Which, in turn does the printing of "unknown" by default.
344 if (toprint & PRINT_HARDWARE_PLATFORM)
345 {
346 char const *element = unknown;
347 #if HAVE_SYSINFO && defined SI_PLATFORM
348 {
349 static char hardware_platform[257];
350 if (0 <= sysinfo (SI_PLATFORM,
351 hardware_platform, sizeof hardware_platform))
352 element = hardware_platform;
353 }
354 #endif
If i am not mistaken (i might be) HAVE_SYSINFO should be in sys/systeminfo.h, and that file is not present by default in arch. That does not necessarily mean that it was not there when the package was compiled. Yet, it shows that, most likely, the packager did not bother compiling the package with HAVE_SYSINFO properly setup. Which is acceptable since it is a non-portable option.
See my comment about gcc -v, my arch evaluates it correctly to Target: x86_64-pc-linux-gnu. But that has nothing to do with uname, uname sends system calls to print information about the system, gcc has the target compiled into it.
Note: saying that uname is part of coreutils is not 100% correct. uname is part of the POSIX standard, yet the -i (--hardware-platform) option to uname is not part of that specification. -i is only implemented by the coreutils package (hell, *BSD systems have -i but it has a completely different meaning there).
Fromt he Mannul of fcntl in solaris, Upon successful completion, value returned for F_SETLKW will be "Value other than -1".
But Apache httpd 1.3.41 source code (http_main.c) check if the returned value is positive like:
int ret;
while ((ret = fcntl(lock_fd, F_SETLKW, &unlock_it)) < 0 && errno == EINTR) {
/* nop */
}
if (ret < 0) {
ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
"fcntl: F_SETLKW: Error getting accept lock, exiting! "
"Perhaps you need to use the LockFile directive to place "
"your lock file on a local disk!");
clean_child_exit(APEXIT_CHILDFATAL);
}
In very rare case, apache in one of our system will exit beacuse of this failed test. I suspect this was caused by a negative value less than -1 returned by fcntl.
So when will fcntl in solaris return a value less than -1?
in your code sample, fcntl returns <0 (e.g. -1 you know) means might have errors if errno was not EINTR, and if errno == EINTR (interrupted), it is not an error, just suggest retrying again.
"Fromt he Mannul of fcntl in solaris, Upon successful completion, value returned for F_SETLKW will be Value other than -1", meant returns 0 or >0 when success, ">=0" is a value other than -1, not <-1 as you guessed.