uname Syscall Missing Operating System String Present in uname -a Output - c

When I run uname -a on the command line, I get the following output:
Linux raspberrypi 5.10.63-v7l+ #1459 SMP Wed Oct 6 16:41:57 BST 2021 armv7l GNU/Linux
This is achieved by the -a parameter which is equivalent to using these parameters (there are 6) -snrvmo.
I am trying to replicate this using the uname() syscall in C. The manpage says the following about my uname() struct that is returned:
DESCRIPTION
uname() returns system information in the structure pointed to by buf. The utsname struct is de‐
fined in <sys/utsname.h>:
struct utsname {
char sysname[]; /* Operating system name (e.g., "Linux") */
char nodename[]; /* Name within "some implementation-defined
network" */
char release[]; /* Operating system release (e.g., "2.6.28") */
char version[]; /* Operating system version */
char machine[]; /* Hardware identifier */
#ifdef _GNU_SOURCE
char domainname[]; /* NIS or YP domain name */
#endif
};
You'll notice there is no operating system string corresponding to the the command line uname -o option. uname --help shows there is a -o parameter to display the OS and that doesn't seem to be available in the struct returned by the uname() syscall.
-o, --operating-system
print the operating system
So the best I can seem to do is get the following information using the syscall noting that "GNU/Linux" isn't at the end like what is given by uname -a:
Linux raspberrypi 5.10.63-v7l+ #1459 SMP Wed Oct 6 16:41:57 BST 2021 armv7l
Is there a way I can get the OS name (in this case, "GNU/Linux") in my C program like I can using uname -o?
My source code is essentially this

You can read the uname code here: https://github.com/MaiZure/coreutils-8.3/blob/master/src/uname.c
In that code, is written:
if (toprint
& (PRINT_KERNEL_NAME | PRINT_NODENAME | PRINT_KERNEL_RELEASE
| PRINT_KERNEL_VERSION | PRINT_MACHINE))
{
struct utsname name;
if (uname (&name) == -1)
die (EXIT_FAILURE, errno, _("cannot get system name"));
if (toprint & PRINT_KERNEL_NAME)
print_element (name.sysname);
if (toprint & PRINT_NODENAME)
print_element (name.nodename);
if (toprint & PRINT_KERNEL_RELEASE)
print_element (name.release);
if (toprint & PRINT_KERNEL_VERSION)
print_element (name.version);
if (toprint & PRINT_MACHINE)
print_element (name.machine);
}
We can understand that in that word: "If have to print kernel info, or node or machine, use uname syscall".
The Operating system is printed latter:
if (toprint & PRINT_OPERATING_SYSTEM)
print_element (HOST_OPERATING_SYSTEM);
The HOST_OPERATING_SYSTEM is defined in gnulib
Is there a way I can get the OS name (in this case, "GNU/Linux") in my C program like I can using uname -o?
Since one compiled software can only be used by one OS, you can imagine to set it at build time.

Related

Why can't I read /proc/pid/mem when I have read permission?

I would like to read the content of the memory file associated to one of my process (with PID 2614). I started to write a very small C program to check I can open it:
#include <stdio.h>
#include <errno.h>
int main() {
FILE* f = fopen("/proc/2614/mem", "rb");
if(!f) {
printf("Error %d\n", errno);
return -1;
}
fclose(f);
return 0;
}
When I run it I get a permission denied error:
$ gcc -o read read.c && ./read
Error 13
However, I have read permission:
$ whoami
pierre
$ ll /proc/2614/mem
-rw------- 1 pierre pierre 0 août 18 19:44 /proc/2614/mem
What's going on?
I know it is possible to read the file because I am able to do it on another system (and some other SO answers already did that). Here is information about my system:
$ uname -a
Linux pierre-computer 4.15.0-112-generic #113~16.04.1-Ubuntu SMP Fri Jul 10 04:37:08 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
And the information for the system on which I can read it:
$ uname -a
Linux not-pierre-computer 3.2.0-4-686-pae #1 SMP Debian 3.2.89-2 i686 GNU/Linux
Are there restrictions on some systems?

Determine `OSTYPE` during runtime in C program

In a C program, I need to find the OSTYPE during runtime, on the basis of which I will do some operations.
Here is the code
#include <stdlib.h>
#include <string.h>
int main () {
const char * ostype = getenv("OSTYPE");
if (strcasecmp(ostype, /* insert os name */) == 0) ...
return 0;
}
But getenv returns NULL (and there is segmentation fault). When I do a echo $OSTYPE in the terminal it prints darwin15 . But when I do env | grep OSTYPE nothing gets printed, which means it is not in the list of environment variables. To make it work on my local machine I can go to the .bash_profile and export the OSTYPE manually but that doesn't solve the problem if I want to run a generated executable on a new machine.
Why is OSTYPE available while running terminal, but apparently not there in the list of environment variables. How to get around this ?
For the crash, you should check if the return was NULL or not before using it in strcmp or any function. From man 3 getenv:
The getenv() function returns a pointer to the value in the
environment, or NULL if there is no match.
If you're at POSIX (most Unix's and somehow all Linux's), I agree with Paul's comment on uname.
But actually you can check for OSTYPE at compile time with precompiler (with #ifdef's), here's a similar question on so: Determine OS during runtime
Edit: uname
Good point Jonathan. man 2 uname on my linux tells how to use (and begin POSIX, macos has the same header, too):
SYNOPSIS
#include <sys/utsname.h>
int uname(struct utsname *buf);
DESCRIPTION
uname() returns system information in the structure pointed to by buf. The utsname struct is
defined in :
struct utsname {
char sysname[]; /* Operating system name (e.g., "Linux") */
char nodename[]; /* Name within "some implementation-defined
network" */
char release[]; /* Operating system release (e.g., "2.6.28") */
char version[]; /* Operating system version */
char machine[]; /* Hardware identifier */
#ifdef _GNU_SOURCE
char domainname[]; /* NIS or YP domain name */
#endif
};

how to use settimeofday(2)?

What am I doing wrong here? I expect settimeofday() to change the system time, not return EINVAL.
$ uname -a
Linux io 4.3.5-300.fc23.x86_64 #1 SMP Mon Feb 1 03:18:41 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
$ cat settimeofday.c
#include <sys/time.h>
#include <stdio.h>
int main()
{
struct timeval tv = {0, 0};
if (settimeofday(&tv, 0) == -1)
perror("settimeofday");
}
$ gcc settimeofday.c
$ sudo ./a.out
settimeofday: Invalid argument
The error is coming from a Thinkpad T450 running Fedora 23. The same code runs fine on OS X.
EDIT
To clarify, the command is being executed as root:
# whoami
root
# sudo ./a.out
settimeofday: Invalid argument
As expected, I get EPERM not EINVAL if I run the program as a regular user:
$ ./a.out
settimeofday: Operation not permitted
Commit e1d7ba was introduced to the Linux kernel in mid-2015 and restricts the value of the tv_sec field. The restriction is influenced by system uptime -- see the commit message and related LKML discussion for details.
That's what was causing the settimeofday call to return EINVAL and explains why the code runs on OS X and older Linux machines.
As shown in the manpage of settimeofday().
If either tv or tz is NULL, the corresponding structure is not set or returned.

System call stat() converted to stat64() without any cpp option

I am running 32-bit SUSE Linux with kernel level 3.0.76.
I can see a stat() call in my code translate to stat64() in the strace output, without me specifying any CPP options like _LARGEFILE64_SOURCE or _FILE_OFFSET_BITS=64.
#include<stdio.h>
#include<sys/stat.h>
int main ( int argc, char * argv[] )
{
char * path = "nofile";
struct stat b;
if (stat(path, &b) != 0) {
}
}
I compiled this file with gcc with no compiler options/flags.
On running the program, relevant strace output is:
munmap(0xb770a000, 200704) = 0
stat64("nofile", 0xbfb17834) = -1 ENOENT (No such file or directory)
exit_group(-1)
Can anyone please tell me how the stat() was converted to stat64() ?
Thanks in advance!
The answer seems to be found in the stat man page
Over time, increases in the size of the stat structure have led to
three successive versions of stat(): sys_stat() (slot __NR_oldstat),
sys_newstat() (slot __NR_stat), and sys_stat64() (new in kernel 2.4;
slot __NR_stat64). The glibc stat() wrapper function hides these
details from applications, invoking the most recent version of the
system call provided by the kernel, and repacking the returned
information if required for old binaries. Similar remarks apply for
fstat() and lstat().
Basically, glibc always call stat64.
If you add a printf("%zu\n", sizeof b); , the struct stat sizes are likely different depending on whether you use _FILE_OFFSET_BITS=64 or not, and glibc converts the struct stat between the kernel and your code.
I can see a stat() call in my code translate to stat64() in the strace output
You need to analyze the way syscall stat is wrapped on your system http://sourceware.org/glibc/wiki/SyscallWrappers (one way that you can see this code is to use gdb: execute this command under gdb: catch syscall stat and when you program will stop on this breakpoint get backtrace and analyze the function that called stat).
From http://linux.die.net/man/2/stat64
Over time, increases in the size of the stat structure have led to
three successive versions of stat(): sys_stat() (slot __NR_oldstat),
sys_newstat() (slot __NR_stat), and sys_stat64() (new in kernel 2.4;
slot __NR_stat64). The glibc stat() wrapper function hides these
details from applications, invoking the most recent version of the
system call provided by the kernel, and repacking the returned
information if required for old binaries.
So it is glibc_wrapper that decides based on sizeof(struct stat) that it should call why stat64.

How can I know the bit count of a cpu/os in C

I know how to get the bit count of a cpu or an operation system with shell.
cat /proc/cpuinfo | grep lm #-> get bit count of a cpu
uname -a #-> get bit count of an operation system
However, how can we get the bit count of those in a C program.
This is an interview question and my solution is as follow:
int *ptr;
printf("%d\n", sizeof(ptr)*8);
But the interviewer said that was wrong. So, what is the correct answer?
POSIX provides a C function uname as well. You can get similar result like the shell command uname:
#include <stdio.h>
#include <sys/utsname.h>
int main(){
struct utsname buf;
uname(&buf);
printf("sysname: %s\nversion: %s\nmachine: %s\n ", buf.sysname, buf.version, buf.machine);
return 0;
}
Output on my machine:
sysname: Linux
version: #1 SMP Tue Oct 2 22:01:37 EDT 2012
machine: i686
On Linux, a simple way is to do e.g. popen with the uname -m command and parse the output.
Another way is to look at the source for the uname command (as it's readily available) and implement something based on that directly.

Resources