How do I get the User's operating system? - c

I am writing a C program, part of which requires me to get the User's operating system. For example, to get the home directory I can do the following:
const char *homedir;
homedir = getpwuid(getuid())->pw_dir;
printf("Home dir: %s\n",homedir);
I can not locate anything related to the OS in pwd.h is there a different file I should be looking at ?

Most of the times (it's not guaranteed), a C compiler defines macros that identify the current operating system (the macro-family, at the very least).
Eg. to identify Windows, Linux and Mac you could do:
#import <stdio.h>
typedef enum {
UNKNOWN,
WINDOWS,
LINUX,
OSX
} OS;
OS current_os();
int main() {
printf("%d",current_os());
return 0;
}
OS current_os() {
#ifdef _WIN32
return WINDOWS;
#elif (defined(__APPLE__) && defined(__MACH__))
return OSX;
#elif defined(__gnu_linux__)
return LINUX;
#else
return UNKNOWN;
#endif
}
On my Mac this prints 3, like it's supposed to do.
You can find a comprehensive list of those macros here.

On SVr4 and POSIX.1-2001 and later compliant UNIXes, use uname() -- for more info, man 2 uname to info about which UNIX, kernel version, and hardware identifier you are on.
Not sure for non-UNIX.
http://man7.org/linux/man-pages/man2/uname.2.html

On Windows, you'll use Win API functions to get exact version (such as GetVersionEx API call).
On Linux - you can use uname(struct utsname *buf) call (man 2 uname).
Other platforms may provide different APIs.
If you want to be portable across operating systems, you should provide a layer of abstraction that hides OS-specific calls and compile-in proper platform-specific implementation that shares a common interface.
Abstractions - this is The Way in cross-platform programming.
Alternatively, you may use pre-processor, which provides some basic platform information at compile-time. Preprocessor is used quite often to select platform-specific code to compile.

Related

access a POSIX function using dlopen

POSIX 2008 introduces several file system functions, which rely on directory descriptor when determining a path to the file (I'm speaking about -at functions, such as openat, renameat, symlinkat, etc.). I doubt if all POSIX platforms support it (well, at least the most recent versions seem to support) and I'm looking for a way to determine if platform supports such functions. Of course one may use autoconf and friends for compile-time determination, but I'm looking for a possibility to find out whether implementation supports -at functions dynamically.
The first that comes to my mind is a dlopen()/dlsym()/dlclose() combo; at least I've successfully loaded the necessary symbols from /usr/libc.so.6 shared library. However, libc may be (or is?) named differently on various platforms. Is there a list of standard locations to find libc? At least on Linux /lib/libc.so appears to be not a symbolic link to shared library, but a ld script. May be there exist some other way to examine during runtime if a POSIX function is supported? Thanks in advance!
#define _GNU_SOURCE 1
#include <dlfcn.h>
#include <stdio.h>
int main ()
{
void * funcaddr = dlsym(RTLD_DEFAULT, "symlinkat");
/* -----------------------^ magic! */
printf ("funcaddr = %p\n", funcaddr);
}
Output:
funcaddr = 0x7fb62e44c2c0
Magic explanation: your program is already linked with libc, no need to load it again.
Note, this is actually GNU libc feature, as hinted by _GNU_SOURCE. POSIX reserves RTLD_DEFAULT "for future use", and then proceeds to define it exactly like GNU libc does. So strictly speaking it is not guaranteed to work on all POSIX systems.

Is there a macro definition to check the Linux kernel version?

I'm wondering if there is a gcc macro that will tell me the Linux kernel version so I can set variable types appropriately. If not, how would I go about defining my own macro that does this?
The linux/version.h file has a macro called KERNEL_VERSION which will let you check the version you want against the current linux headers version (LINUX_VERSION_CODE) installed. For example to check if the current Linux headers are for kernel v2.6.16 or earlier:
#include <linux/version.h>
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)
...
#else
...
#endif
A better way to get the version information at run-time is to use the utsname function in include/linux/utsname.h.
char *my_kernel_version = utsname()->release;
This is essentially how /proc/version gets the current kernel verison.
See also
Getting kernel version from linux kernel module at runtime
gcc won't know this information. As an alternative, you can determine a lot of kernel information at runtime easily.
You can define your runtime type like
struct unified_foo {
unsigned int kernel_version;
union {
kernel_x_foo_type k_x;
kernel_y_foo_type k_y;
kernel_z_foo_type k_z;
} u;
};
and have code at runtime look at /proc/version (or whatever you need from the kernel runtime environment) and set kernel_version approriately. The kernel_x_foo_type et al. is your type that you want to be conditional on the kernel version. The calling code needs to look at kernel_version and access the appropriate u.k_x, u.k_y, or u.k_z data.
In kernel code first Makefile. You will find version-related variables.
VERSION = 4
PATCHLEVEL = 9
SUBLEVEL = 37

Determine OS during runtime

Neither ISO C nor POSIX offer functionality to determine the underlying OS during runtime. From a theoretical point of view, it doesn't matter since C offers wrappers for the most common system calls, and from a nit-picking point of view, there doesn't even have to be an underlying OS.
However, in many real-world scenarios, it has proven helpful to know more about the host environment than C is willing to share, e.g. in order to find out where to store config files or how to call select(), so:
Is there an idiomatic way for an application written in C to determine the underlying OS during runtime?
At least, can I easily decide between Linux, Windows, BSD and MacOS?
My current guess is to check for the existence of certain files/directories, such as C:\ or /, but this approach seems unreliable. Maybe querying a series of such sources may help to establish the notion of "OS fingerprints", thus increasing reliability. Anyway, I'm looking forward to your suggestions.
Actually, most systems have a uname command which shows the current kernel in use. On Mac OS, this is usually "Darwin", on Linux it's just plain "Linux", on Windows it's "ERROR" and FreeBSD will return "FreeBSD".
More complete list of uname outputs
I'm pretty sure that there's a C equivalent for uname, so you won't need system()
IF you are on a POSIX system, you can call uname() from <sys/utsname.h>.
This obviously isn't 100% portable, but I don't think there will be any method that can grant that at runtime.
see the man page for details
Runtime isn't the time to determine this, being that without epic kludges binaries for one platform won't run on another, you should just use #ifdefs around the platform sensitive code.
The accepted answer states uname, but doesn't provide a minimal working example, so here it is for anyone interested-hope it will save you the time it took for me:
#include <stdio.h>
#include <stdlib.h>
#include <sys/utsname.h>
int main(void) {
struct utsname buffer;
if (uname(&buffer) != 0) {
perror("uname");
exit(0);
}
printf("OS: %s\n", buffer.sysname);
return 0;
}
(Possible) Output:
OS: Linux
PS: Unfortunately, this uses a POSIX header: Compilation fails due to missing file sys/utsname.h, which most probably won't work in Windows.
if (strchr(getenv("PATH"),'\\'))
puts("You may be on windows...");
Even do I agree that "Runtime isn't the time to determine this..."

How to use Linux-specific APIs and libraries only on Linux builds with CMake?

I have a project that I run on Linux (primarily), but sometimes on Darwin/Mac OS X. I use CMake to generate Makefiles on Linux and an Xcode project on Mac OS X. So far, this has worked well.
Now I want to use some Linux-specific functions (clock_gettime() and related functions). I get linker errors on Mac OS X when I try to use clock_gettime(), so I assume it is only available on Linux. I am prepared to introduce conditionally-compiled code in the .c files to use clock_gettime() on Linux and plain old clock() on Mac OS. (BTW I was planning to use #include <unistd.h> and #if _POSIX_TIMERS > 0 as the preprocessor expression, unless someone has a better alternative.)
Things get tricky when it comes to the CMakeLists.txt file. What is the preferred way of introducing linkage to Linux-specific APIs only under the Linux build in a cross-platform CMake project?
Note: An earlier revision of this question contained references to glibc, which was overly specific and confusing. The question is really about the right way to use Linux-specific APIs and libraries in a cross-platform CMake project.
Abstracting away from your examples, and answering only this question:
How to use Linux-specific APIs and libraries only on Linux builds with
CMake?
CMake provides numerous useful constants that you can check in order to determine which system you are running:
if (${UNIX})
# *nix-specific includes or actions
elsif (${WIN32})
# Windows-specific includes or actions
elsif (${APPLE})
# ...
endif (${UNIX})
(I know you're asking about glibc, but you really want to know whether clock_gettime is present, right? But nothing in your question is Linux-specific...)
If you want to check for clock_gettime, you can use the preprocessor. If clock_gettime is present, then _POSIX_TIMERS will be defined. The clock_gettime function is part of an optional POSIX extension (see spec), so it is not Linux-specific but not universal either. Mac OS X does not have clock_gettime: it is not declared in any header nor defined in any library.
#include <time.h>
#include <unistd.h> /* for _POSIX_TIMERS definition, if present */
#if _POSIX_TIMERS
...use clock_gettime()...
#else
...use something else...
#endif
This doesn't solve the problem that you still have to link with -lrt on Linux. This is typically solved with something like AC_CHECK_LIB in Autoconf, I'm sure there's an equivalent in CMake.
From man 2 clock_gettime:
On POSIX systems on which these functions are available, the symbol _POSIX_TIMERS is defined in <unistd.h> to a value greater than 0. The symbols _POSIX_MONOTONIC_CLOCK, _POSIX_CPUTIME, _POSIX_THREAD_CPUTIME indicate that CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID are available. (See also sysconf(3).)
On Darwin you can use the mach_absolute_time function if you need a high-resolution monotonic clock. If you don't need the resolution or monotonicity, you should probably be using gettimeofday on both platforms.
There is also built-in CMake macro for checking if symbol exists - CheckSymbolExists.

Most standard way to select a function name depending on platform?

I am currently using the popen function in code that is compiled by two compilers: MS Visual Studio and gcc (on linux). I might want to add gcc (on MinGW) later.
The function is called popen for gcc, but _popen for MSVS, so i added the following to my source code:
#ifdef _MSC_VER
#define popen _popen
#define pclose _pclose
#endif
This works, but i would like to understand whether there exists a standard solution for such problems (i recall a similar case with stricmp/strcasecmp). Specifically, i would like to understand the following:
Is _MSC_VER the right flag to depend on? I chose it because i have the impression that linux environment is "more standard".
If i put these #define's in some header file, is it important whether i #include it before or after stdio.h (for the case of popen)?
If _popen is defined as a macro itself, is there a chance my #define will fail? Should i use a "new" token like my_popen instead, for that reason or another?
Did someone already do this job for me and made a good "portability header" file that i can use?
Anything else i should be aware of?
Better to check for a windows-specific define (_WIN32 perhaps) because mingw won't have it either. popen() is standardised (it's a part of the Single UNIX® Specification v2)
No; so long as the macro is defined before its first use it does not matter if _popen() is not defined until later.
No; what you have is fine even if _popen is a macro.
It's been done many times but I don't know of a freely-licensed version you can use.
The way you are doing it is fine (with the #ifdef etc) but the macro that you test isn't. popen is something that depends on your operating system and not your compiler.
I'd go for something like
#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 2)
/* system has popen as expected */
#elif defined(YOUR_MACRO_TO DETECT_YOUR_OS)
# define popen _popen
# define pclose _pclose
#elif defined(YOUR_MACRO_TO DETECT_ANOTHER_ONE)
# define popen _pOpenOrSo
# define pclose _pclos
#else
# error "no popen, we don't know what to do"
#endif
_MSC_VER is the correct macro for detecting the MSVC compiler. You can use __GNUC__ for GCC.
If you are going to use popen as your macro ID, I suggest you #include it after, because of 3.
If you #include it after stdio.h, it should work AFAIK, but better safe than sorry, no? Call it portable_popen or something.
Many projects (including some of mine) have a portability header, but it's usually better to roll your own. I'm a fan of doing things yourself if you have the time. Thus you know the details of your code (easier to debug if things go wrong), and you get code that is tailored to your needs.
Not that I know of. I do stuff like this all the time, without problems.
Instead of ending up with cluttered files containing #ifdef..#else..#endif blocks, I'd prefer a version using different files for different platforms:
put the OS dependent definitions in one file per platform and #define a macro my_popen
#include this file in your platform-agnostic code
never call the OS functions directly, but the #define that you created (i.e. my_popen)
depending on your OS, use different headers for compilation (e.g. config/windows/mydefines.h on windows and config/linux/mydefines.h on linux, so set the include path appropriate and always #include "mydefines.h")
That's a much cleaner approach than having the OS decision in the source itself.
If the methods you're calling behave different between windows and linux, decide which one shall be the behavior you're using (i.e. either always windows behavior or always linux behavior) and then create wrapper methods to achieve this. For that, you'll also need not only two mydefines.h files but also to myfunctions.c files that reside in the config/OSTYPE directories.
Doing it that way, you also get advantages when it comes to diff the linux and the windows version: you could simply diff two files while doing a diff on the linux and windows blocks of the same file could be difficult.

Resources