8

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.

Philip
  • 5,795
  • 3
  • 33
  • 68
  • 12
    Er, you know this information at compile time – David Heffernan Jan 03 '12 at 01:23
  • How does determining the OS at *runtime* help you write a `select()` call correctly? – Kerrek SB Jan 03 '12 at 01:25
  • +1, @David. At least for differences like Linux vs. Windows vs. Mac OS X, etc. I guess a runtime way to differentiate between different versions of each of those might be what the OP is looking for, but the answer to that would be specific to each operating system, presumably. – Carl Norum Jan 03 '12 at 01:25
  • @David: I don't like `#ifdef`. – Philip Jan 03 '12 at 01:25
  • 4
    Attempt to run the program for some time. If it gets killed in a crash, you're on Windows. If the OS gets updated to 5G or a cat, you're on a Mac. If you get hacked after 180 days, it's Linux. Otherwise it's BSD. – Kerrek SB Jan 03 '12 at 01:26
  • 1
    You do not 'really' know this information at compile-time. You can run windows binaries on wine+linux, you can run linux binaries on solaris, you can run linux binaries on some BSDs. – rsaxvc Jan 03 '12 at 01:27
  • 2
    @Philip Well, `#ifdef` is essential here, you must learn to get along with it even if you cannot bring yourself to like it – David Heffernan Jan 03 '12 at 01:28
  • 3
    @rsaxvc, but as far as your app is concerned, wine+linux *is* windows, running linux binaries on solaris *is* linux, and running them on BSD is *still* linux. – Carl Norum Jan 03 '12 at 01:28
  • 2
    @rsaxvc Then it's up to the emulator to do a good job of being the emulated system. When you are running on Wine the OS is Windows from the point of view of the program. – David Heffernan Jan 03 '12 at 01:29
  • @ David Heffernan: What do you mean? None of those are emulators. – rsaxvc Jan 03 '12 at 01:35
  • @Carl - but you may need to change runtime behaviour of os-specifics to deal with it. Look at the proc filesystem - different on many unices, so you have to deal with it at runtime if you're doing binary compatability. – rsaxvc Jan 03 '12 at 01:36
  • `wine` even tells you by its name: Wine Is Not an Emulator – Philip Jan 03 '12 at 01:36
  • @rsaxvc - sure, but if you're writing an app that you intend to run in these emulated environments, you need to take that into account. If you write a Windows app that doesn't work in Wine, it just means that Wine isn't doing a good enough job. – Carl Norum Jan 03 '12 at 01:38
  • @CarlNorum - I know, that's what I said, which is why you might need to know the OS at runtime instead of compile-time. (Personally, I'd rather just compile it twice and wrap up the os-specifics) – rsaxvc Jan 03 '12 at 01:40
  • 1
    @rsaxvc Sure it's an emulator. It emulates Windows on systems that are not Windows. The WINE people don't like the negative connotations of the word and in particular the expectation that an emulator performs poorly. But it is an emulator in just the same way as WOW64 is an emulator. – David Heffernan Jan 03 '12 at 01:41
  • @DavidHeffernan - Poor example - in WOW64, the CPU is directly executing x86-32 instructions, just like it does on WINE. What WINE is, is a set of libraries that implement the WINAPI on a different backend. No emulation in either (unless you mean WOW64 on Itanium...) – rsaxvc Jan 03 '12 at 01:46
  • @DavidHeffernan - I guess this is really just semantics and not terribly related to the question at hand. – rsaxvc Jan 03 '12 at 01:47
  • 1
    @rsaxvc Ah, you define emulation to mean emulation of hardware. A more general definition, the one I'm using, includes emulation of software. [Microsoft's description of WOW64](http://msdn.microsoft.com/en-us/library/aa384249(v=VS.85).aspx) starts like this: WOW64 is the x86 emulator that allows 32-bit Windows-based applications to run seamlessly on 64-bit Windows. Yes this is not related to Q. Feel free to tidy up. – David Heffernan Jan 03 '12 at 01:48
  • I'm still trying to figure out why you'd dislike `#ifdef`, but be perfectly content to use `if`. Any insight on that? – Cody Gray - on strike Jan 03 '12 at 03:37

5 Answers5

7

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()

Tom van der Woerdt
  • 29,532
  • 7
  • 72
  • 105
  • Looks like I beat all of you :-) – Tom van der Woerdt Jan 03 '12 at 01:31
  • @Tom Are you sure about what happens on Windows? For all C compilers? – David Heffernan Jan 03 '12 at 01:33
  • @DavidHeffernan Haha, I'm sure it *won't* return the string "ERROR". However, the results on Windows vary a lot. As far as I know only cygwin returns a proper uname, but some compilers may actually compile it. Because of this, `uname` is unreliable when ran under Wine or Crossover. – Tom van der Woerdt Jan 03 '12 at 01:34
  • @TomvanderWoerdt - I posted one saying 'you're right, can't be done for all OSs', but then realized the OP was only asking about BSD/OSX/Windows/Linux. – rsaxvc Jan 03 '12 at 01:42
4

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

rejj
  • 1,216
  • 7
  • 13
1

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.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • error C1083: Cannot open include file: 'sys/utsname.h': No such file or directory. On Linux this works, but not on Windows. – txs Dec 06 '18 at 20:17
1

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.

richo
  • 8,717
  • 3
  • 29
  • 47
  • 4
    -1 because the question was about how to determine the operating system at runtime, not whether or not you should do it. – rurouniwallace Jul 19 '14 at 17:35
  • Yeah, but that doesn't help the OP. For the _VAST_ majority of cases an object emitted for FreeBSD won't work on Linux anyway (Which holds for most of his cases) so doing this at runtime is the least of his woes. – richo Aug 03 '14 at 18:48
-2
if (strchr(getenv("PATH"),'\\'))
    puts("You may be on windows...");

Even do I agree that "Runtime isn't the time to determine this..."

BusyProgrammer
  • 2,783
  • 5
  • 18
  • 31