Jessica
Jessica

Reputation: 2035

Getting the OS version in Mac OS X using standard C

I'm trying to get the version of Mac OS X programmatically in C. After searching for a while I tried this code:

#include <CoreServices/CoreServices.h>

int GetOS()
{
    SInt32 majorVersion,minorVersion,bugFixVersion;

    Gestalt(gestaltSystemVersionMajor, &majorVersion);
    Gestalt(gestaltSystemVersionMinor, &minorVersion);
    Gestalt(gestaltSystemVersionBugFix, &bugFixVersion);

    printf("Running on Mac OS X %d.%d.%d\n",majorVersion,minorVersion,bugFixVersion);    

    return 0;
}

XCode returns an LD error:

Undefined symbols for architecture x86_64: "_Gestalt", referenced from: _GetOS in main.o

What am I missing? How do you do this?

I found also this snippet

[[NSProcessInfo processInfo] operatingSystemVersionString]

But I have no idea how to write that in C.

Upvotes: 18

Views: 10056

Answers (5)

Thomas Perl
Thomas Perl

Reputation: 2348

If for whatever reason you want to avoid the Gestalt API (which still works fine, but is deprecated), the macosx_deployment_target.c in cctools contains a code snippet that uses the CTL_KERN + KERN_OSRELEASE sysctl(), similar to other answers here.

Here's a small program adapted from that code and taking macOS 11 and newer (tested and verified with up to macOS 12.6, which was at time of updating this post the latest stable release) into account:

#include <stdio.h>
#include <sys/sysctl.h>

int main()
{
    char osversion[32];
    size_t osversion_len = sizeof(osversion) - 1;
    int osversion_name[] = { CTL_KERN, KERN_OSRELEASE };

    if (sysctl(osversion_name, 2, osversion, &osversion_len, NULL, 0) == -1) {
        printf("sysctl() failed\n");
        return 1;
    }

    uint32_t major, minor;
    if (sscanf(osversion, "%u.%u", &major, &minor) != 2) {
        printf("sscanf() failed\n");
        return 1;
    }

    if (major >= 20) {
        major -= 9;

        // macOS 11 and newer
        printf("%u.%u\n", major, minor);
    } else {
        major -= 4;

        // macOS 10.1.1 and newer
        printf("10.%u.%u\n", major, minor);
    }

    return 0;
}

Upvotes: 1

jetset
jetset

Reputation: 398

Using the hint from @uchuugaka in the comment on the answer by @McUsr, I wrote a function that seems to work. I'm not saying it's better than any other answer.

/*
 * Structure for MacOS version number
 */
typedef struct macos_version_str
{
    ushort major;
    ushort minor;
    ushort point;
} macos_type;

/****************************************************************************
 *
 * Determine the MacOS version.
 *
 * Parameters:
 *    version_struct:  (pointer to) macos_version structure to be filled in.
 *
 * Return value:
 *    0: no error.
 *
 ****************************************************************************/

static int get_macos_version ( macos_type *version_struct )
{
    char    os_temp [20] = "";
    char   *os_temp_ptr  = os_temp;
    size_t  os_temp_len  = sizeof(os_temp);
    size_t  os_temp_left = 0;
    int     rslt         = 0;

    
    version_struct->major = 0;
    version_struct->minor = 0;
    version_struct->point = 0;
    
    rslt = sysctlbyname ( "kern.osproductversion", os_temp, &os_temp_len, NULL, 0 );
    if ( rslt != 0 )
    {
        fprintf ( stderr,
                  "sysctlbyname() returned %d error (%d): %s",
                  rslt, errno, strerror(errno));
        return ( rslt );
    }
    
    os_temp_left = os_temp_len; /* length of string returned */
    int temp = atoi ( os_temp_ptr );
    version_struct->major = temp;
    version_struct->major = atoi ( os_temp_ptr );
    
    while ( os_temp_left > 0 && *os_temp_ptr != '.' )
    {
        os_temp_left--;
        os_temp_ptr++;
    }
    os_temp_left--;
    os_temp_ptr++;
    version_struct->minor = atoi ( os_temp_ptr );
    
    while ( os_temp_left > 0 && *os_temp_ptr != '.' )
    {
        os_temp_left--;
        os_temp_ptr++;
    }
    os_temp_left--;
    os_temp_ptr++;
    version_struct->point = atoi ( os_temp_ptr );
    
    fprintf ( stderr, "Calculated OS Version: %d.%d.%d", version_struct->major, version_struct->minor, version_struct->point );
    
    if ( version_struct->major == 0 ||
         version_struct->minor == 0 ||
         version_struct->point == 0   )
    {
        fprintf ( stderr, "Unable to parse MacOS version string %s", os_temp );
        return ( -2 );
    }
    
    return 0;
}

Upvotes: 2

Jerry Krinock
Jerry Krinock

Reputation: 5030

Here is one with "less work", good enough for home projects (statically allocated buffers, ignoring errors). Works for me in OS X 10.11.1.

#include <stdio.h>

/*!
  @brief    Returns one component of the OS version
  @param    component  1=major, 2=minor, 3=bugfix
 */
int GetOSVersionComponent(int component) {
    char cmd[64] ;
    sprintf(
            cmd,
            "sw_vers -productVersion | awk -F '.' '{print $%d}'",
            component
    ) ;
    FILE* stdoutFile = popen(cmd, "r") ;

    int answer = 0 ;
    if (stdoutFile) {
        char buff[16] ;
        char *stdout = fgets(buff, sizeof(buff), stdoutFile) ;
        pclose(stdoutFile) ;
        sscanf(stdout, "%d", &answer) ;
    }

    return answer ;
}

int main(int argc, const char * argv[]) {
    printf(
           "Your OS version is: %d.%d.%d\n",
           GetOSVersionComponent(1),
           GetOSVersionComponent(2),
           GetOSVersionComponent(3)
           ) ;

    return 0 ;
}

Upvotes: 1

McUsr
McUsr

Reputation: 1409

The code below should work in the foreseeable future for figuring out the current version of Mac Os X.

/*  McUsr put this together, and into public domain, 
    without any guarrantees about anything,
    but the statement that it works for me.
*/

#if 1 == 1
#define TESTING
#endif

#include <sys/param.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct osver {
    int minor;
    int sub;
} ;
typedef struct osver osxver ;
void macosx_ver(char *darwinversion, osxver *osxversion ) ;
char *osversionString(void) ;

#ifdef TESTING
int main( int argc, char *argv[] )
{
    osxver foundver;
    char *osverstr= NULL ;
    osverstr=osversionString() ;
    macosx_ver(osverstr, &foundver ) ;
    printf("Mac os x version = 10.%d.%d\n",foundver.minor,foundver.sub );
    free(osverstr);
    return 0;
}
#endif
char *osversionString(void) {
    int mib[2];
    size_t len;
    char *kernelVersion=NULL;
    mib[0] = CTL_KERN;
    mib[1] = KERN_OSRELEASE;

    if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0 ) {
        fprintf(stderr,"%s: Error during sysctl probe call!\n",__PRETTY_FUNCTION__ );
        fflush(stdout);
        exit(4) ;
    }

    kernelVersion = malloc(len );
    if (kernelVersion == NULL ) {
        fprintf(stderr,"%s: Error during malloc!\n",__PRETTY_FUNCTION__ );
        fflush(stdout);
        exit(4) ;
    }
    if (sysctl(mib, 2, kernelVersion, &len, NULL, 0) < 0 ) {
        fprintf(stderr,"%s: Error during sysctl get verstring call!\n",__PRETTY_FUNCTION__ );
        fflush(stdout);
        exit(4) ;
    }

    return kernelVersion ;
}

void macosx_ver(char *darwinversion, osxver *osxversion ) {
/*
    From the book Mac Os X and IOS Internals:
    In version 10.1.1, Darwin (the core OS) was renumbered from v1.4.1 to 5.1,
    and since then has followed the OS X numbers consistently by being four
    numbers ahead of the minor version, and aligning its own minor with the
    sub-version.
*/
    char firstelm[2]= {0,0},secElm[2]={0,0};

    if (strlen(darwinversion) < 5 ) {
        fprintf(stderr,"%s: %s Can't possibly be a version string. Exiting\n",__PRETTY_FUNCTION__,darwinversion);
        fflush(stdout);
        exit(2);
    }
    char *s=darwinversion,*t=firstelm,*curdot=strchr(darwinversion,'.' );

    while ( s != curdot )
        *t++ = *s++;
    t=secElm ;
    curdot=strchr(++s,'.' );
    while ( s != curdot )
        *t++ = *s++;
    int maj=0, min=0;
    maj= (int)strtol(firstelm, (char **)NULL, 10);
    if ( maj == 0 && errno == EINVAL ) {
        fprintf(stderr,"%s Error during conversion of version string\n",__PRETTY_FUNCTION__);
        fflush(stdout);
        exit(4);
    }

    min=(int)strtol(secElm, (char **)NULL, 10);

    if ( min  == 0 && errno == EINVAL ) {
        fprintf(stderr,"%s: Error during conversion of version string\n",__PRETTY_FUNCTION__);
        fflush(stdout);
        exit(4);
    }
    osxversion->minor=maj-4;
    osxversion->sub=min;
}

Upvotes: 1

user7116
user7116

Reputation: 64098

Did you pass the appropriate framework to GCC in order to enable CoreServices?

% gcc -framework CoreServices -o getos main.c

Upvotes: 18

Related Questions