Getting the OS version in Mac OS X using standard C - 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.

Did you pass the appropriate framework to GCC in order to enable CoreServices?
% gcc -framework CoreServices -o getos main.c

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;
}

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 ;
}

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;
}

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;
}

Related

C program weird behavior

Hello guys this is my first question on stack
I am very noob to c language but i am decompling simple crackme me from crackme.one
https://crackmes.one/crackme/62072dd633c5d46c8bcbfd9b
I opened it in Ida and get this source c code
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // eax
char nptr; // [rsp+1Fh] [rbp-11h] BYREF
int v6; // [rsp+20h] [rbp-10h]
int i; // [rsp+24h] [rbp-Ch]
if ( argc != 2 )
{
printf("Usage : %s <license pass code here [numbers only]>\n", *argv);
exit(0);
}
v6 = 0;
for ( i = 0; ; ++i )
{
v4 = strlen(argv[1]);
if ( i >= v4 )
break;
nptr = argv[1][i];
v3 = atoi(&nptr);
v6 += v3;
}
if ( v6 == 50 )
{
puts("Premium access has been activated !");
exit(0);
}
printf("Wrong license code");
exit(0);
}
I easyly discovered that key code must be serial of numbers that their sum is 50 i wrote a python program to guess the key and the weird is key like 5555555555 works but not 55555555532 although sum is 50
Please someone point out to where is this behavior coming from ?

Using pcre_get_substring_list, match all pattern inside of string and return in array in C with PCRE?

I need to use C in Linux with PCRE to match in this string "<test>a</test> <test>b</test> <test>c</Test>" to get the letters a, b, and c.
I found this script in stackoverflow, it is good but does not work for all the matches. Only the first matches. Why?
/*
* gcc pcre1.c -lpcre
*/
#include <pcre.h>
#include <stdio.h>
#include <string.h>
int main()
{
pcre* compile;
pcre_extra* extra;;
int res;
int ovector[30];
const char* pattern="(?i)<test>(.*?)</test>";
const char* errptr;
const char* match[30];
const char** match_list = match;
int erroffset;
char* test_str = "<test>a</test> <test>b</test> <test>c</Test>";
compile = pcre_compile(pattern, PCRE_MULTILINE,&errptr,&erroffset,NULL);
if ( compile == NULL ) {
fprintf(stderr, "ERROR: Could not compile '%s' : %s\n", pattern, errptr);
exit(1);
}
extra = pcre_study(compile, 0, &errptr);
if ( errptr != NULL ) {
fprintf(stderr, "ERROR: Could not study '%s' : %s\n", pattern, errptr);
exit(1);
}
res = pcre_exec(compile,extra,test_str,strlen(test_str),0,0,ovector,sizeof(ovector));
if ( res == 0 ) {
res = 30/3;
}
if ( res > 0 ) {
pcre_get_substring_list(test_str, ovector, res, &match_list);
printf("buffer : %s\n", test_str);
printf("match :\n");
for ( int i = 0; match_list[i]; ++ i ) {
printf("%9s%s\n", " ", match_list[i]);
printf("\n");
}
if ( match_list )
pcre_free_substring_list(match_list);
}
printf("\n");
if (compile)
pcre_free(compile);
if (extra)
pcre_free(extra);
}```
thanks
I changed your code slightly, but this works as you expect now:
% ./pcre1
a
b
c
I'll list the changes and why I made them:
We will be using ovector before it it is set initially, so zero out.
int ovector[30] = {0};
The pcre_get_substring() will be easier for you to use for this purpose, so I switched away from pcre_get_substring_list().
We didn't need match[], as pcre_get_substring() calls pcre_malloc().
The variable match_list must be char* as we are passing it as &match_list.
const char* match_list;
The function pcre_exec() expects ovecsize to be a multiple of 3.
3*(sizeof(ovector)/3)
I wrapped the pcre_exec() call in a while loop.
I used pcre_get_substring(), printf(), and pcre_free_substring() instead.
// gcc pcre1.c -lpcre
#include <pcre.h>
#include <stdio.h>
#include <string.h>
int main()
{
pcre* compile;
pcre_extra* extra;;
int res;
int ovector[30] = {0};
const char* pattern="(?i)<test>(.*?)</test>";
const char* errptr;
const char* match_list;
int erroffset;
char* test_str = "<test>a</test> <test>b</test> <test>c</Test>";
compile = pcre_compile(pattern, PCRE_MULTILINE,&errptr,&erroffset,NULL);
if ( compile == NULL ) {
fprintf(stderr, "ERROR: Could not compile '%s' : %s\n", pattern, errptr);
exit(1);
}
extra = pcre_study(compile, 0, &errptr);
if ( errptr != NULL ) {
fprintf(stderr, "ERROR: Could not study '%s' : %s\n", pattern, errptr);
exit(1);
}
while ((res = pcre_exec(compile, extra, test_str, strlen(test_str), ovector[1], 0, ovector, 3*(sizeof(ovector)/3))) >= 0) {
if (pcre_get_substring(test_str, ovector, res, 1, &match_list) >= 0) {
printf("%s\n", match_list);
pcre_free_substring(match_list);
}
}
if (compile)
pcre_free(compile);
if (extra)
pcre_free(extra);
}

Trying to compile a x86 C program on a x64 RedHat environment [Part 2]

Still following the saga of a Cobol developer handling C programs on a environment migration.
I think we could manage 90% of the problems so far and most of our C programs are now compiling fine on the RHEL 64 bits.
Friday we found another module that is not compiling and I hope to be the last one.
I am receiving two warnings, but I have no idea about it and our make does not allow us to compile it properly.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <memory.h>
#include <errno.h>
#include </public/prod/src/mfqinc.h>
static FILE *infile = NULL;
static FILE *outfile = NULL;
char inbuf[1024], blockrec[10240];
static char workstring[1024];
static char workstring0[1024];
static char gra2533h [8];
static char gra2401h [8] ;
static char gra2501h [8] ;
static char gra2506h [8] ;
static char crtn[1] ; /* {"0x0A"};*/
int i;
int z;
int f;
int resulisn;
int j;
int d;
int ctrrec;
int nb_ecrit;
static int longueur;
void initworkstring()
{
extern char workstring [1024];
int ik;
for (ik=0; ik < 1024 ;ik++)
{
workstring [ik] = NULL;
}
}
void ecriture()
{
extern char workstring [1024];
extern int longueur;
extern int ctrrec;
nb_ecrit = fwrite(workstring,sizeof(char),longueur,outfile);
ctrrec++;
if ( nb_ecrit != longueur )
{
printf("andlog:erreur d ecriture ");
printf("Ecrit: %i",nb_ecrit);
printf("Erreur no: %i",errno);
exit(60);
}
}
static void errarg(char *errmsg)
{
fprintf(stderr,"ERROR - %s\nCommand format :\n\t",errmsg);
fprintf(stderr," : \n");
/* exit(1); */
}
main (int argc, char *argv[])
{
if ( argc < 3 )
{
printf("<<< Programme : and015 >>>\n");
printf("<<< Nombre de parametres incorrect >>>\n");
printf("<<< Remove catacteres speciaux >>>\n");
printf("<<< Param # 1 = nom du fichier d'input >>>\n");
printf("<<< Param # 2 = nom du fichier d'output >>>\n");
printf("\n");
exit(10);
}
if ((infile = fopen(argv[1],"rb")) == NULL )
{
printf("Erreur sur fichier input \n");
exit(20);
}
if ((outfile = fopen(argv[2],"wb")) == NULL )
{
printf("Erreur sur fichier output \n");
exit(30);
}
initworkstring();
/* remplir les table de catacteres */
crtn[0]=0x0a;
gra2533h[0]=0x1b;
gra2533h[1]=0x5b;
gra2533h[2]=0x32;
gra2533h[3]=0x35;
gra2533h[4]=0x3b;
gra2533h[5]=0x33;
gra2533h[6]=0x33;
gra2533h[7]=0x48;
gra2401h[0]=0x1b;
gra2401h[1]=0x5b;
gra2401h[2]=0x32;
gra2401h[3]=0x34;
gra2401h[4]=0x3b;
gra2401h[5]=0x30;
gra2401h[6]=0x31;
gra2401h[7]=0x48;
gra2501h[0]=0x1b;
gra2501h[1]=0x5b;
gra2501h[2]=0x32;
gra2501h[3]=0x35;
gra2501h[4]=0x3b;
gra2501h[5]=0x30;
gra2501h[6]=0x31;
gra2501h[7]=0x48;
gra2506h[0]=0x1b;
gra2506h[1]=0x5b;
gra2506h[2]=0x32;
gra2506h[3]=0x35;
gra2506h[4]=0x3b;
gra2506h[5]=0x30;
gra2506h[6]=0x36;
gra2506h[7]=0x48;
while (fgets(inbuf,1024,infile) != NULL )
{
i=0;
j=0;
d=0;
for (i=0; i < 1024 ; i++)
{
if ( inbuf [i] == NULL )
{
i = 9999;
}
else
{
if ( inbuf[i] == 0x1b )
{
i++;
d=0;
for (d=0 ;d < 8; d++)
{
if ( inbuf[i] == 0x48 )
{
j= j-d;
workstring[j]=0x0a;
d=99;
}
else
{
if (inbuf[i] == 0x6d)
{
j= j-d;
workstring[j]=0x0a;
d=99;
}
else
{
if (inbuf[i] == 0x53)
{
j=j-d;
workstring[j]=0x0a;
d=99;
}
else
{
workstring[j] = inbuf[i];
j++;
i++;
}
}
}
} /* end du for*/
}/*fin du if 01b*/
else
{
workstring[j] = inbuf[i];
j++;
}
}
/* mettre dans workstring */
workstring [j] = inbuf [i];
}
strcat(workstring,crtn);
longueur = j ;
ecriture ();
initworkstring();
} /* fin du while */
fclose(infile);
fclose(outfile);
exit(0);
}
Those are the warnings I am having.
/exp/prod/src>gcc -m64 mfqlog.c -o mfqlog
mfqlog.c: In function 'initworkstring':
mfqlog.c:48:20: warning: assignment makes integer from pointer without a cast [enabled by default]
workstring [ik] = NULL;
^
mfqlog.c: In function 'main':
mfqlog.c:146:18: warning: comparison between pointer and integer [enabled by default]
if ( inbuf [i] == NULL )
Really sorry to bother with this question. But I really need to compile this code.
The funny fact is that the program is working (probably, for some unknown reason to me, the program was compiling before and it is not anymore due to changes on the compiler maybe?!?) on our actual environment, but if we try to compile it will fail and the only way to make it working is restoring the executable backup.
As we are moving to a new 64 bits environment, it has to be recompiled.
Thank you all for the help.
NULL is defined as (void*)0. Replace NULL with '\0' or 0.
workstring [ik] = '\0';
if ( inbuf [i] == '\0' )

'undefined reference to' [OpenBSD 3.5 system defined method]

I have been learning how to use Unix functions to program in C so that I can program Semaphore functionality by scratch (without pthreads), but I am currently stuck. The man pages told me to include particular header files to use functions of interest (such as malloc, tsleep, wakeup, etc.), but when I try to run my program with the headers and method calls, I receive the following errors:
/tmp//ccg29960.o: In function `allocate_semaphore':
/tmp//ccg29960.o(.text+0x28d): undefined reference to `simple_lock_init'
/tmp//ccg29960.o: In function `down_semaphore':
/tmp//ccg29960.o(.text+0x2fb): undefined reference to `tsleep'
/tmp//ccg29960.o: In function `up_semaphore':
/tmp//ccg29960.o(.text+0x3b5): undefined reference to `wakeup'
/tmp//ccg29960.o: In function `free_semaphore':
/tmp//ccg29960.o(.text+0x43b): undefined reference to `simple_lock'
/tmp//ccg29960.o(.text+0x4af): undefined reference to `simple_unlock'
collect2: ld returned 1 exit status
The relevant code is below:
//#include <stdio.h>
//#include <stdlib.h>
#include <sys/errno.h>
#include <sys/queue.h>
//#include <sys/time.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/lock.h>
struct entry
{
pid_t id;
SIMPLEQ_ENTRY(entry) next;
} *np;
typedef struct
{
const char* name;
pid_t process;
pid_t p_process; //parent process
int count;
SIMPLEQ_HEAD(queuehead,entry) head;
struct simplelock *slock;
} named_semaphore;
named_semaphore* s_list[64];
int num_semaphores = 0;
int main()
{
//lockinit(0, 0, 0,0, 0);
printf("Hello world\n");
return 0;
}
//... irrelevant code elided
int allocate_semaphore( const char* name, int initial_count )
{
int num_elements, i;
named_semaphore *new_s;
//perform initial checks before creating a new semaphore
//make sure the given name is an acceptable length
num_elements = sizeof(name) / sizeof(*name);
if ( num_elements > 32 )
{
return ENAMETOOLONG;
}
//make sure the given name is unique to this process
for (i = 0; i < num_semaphores; i++)
{
if (s_list[i]->process == getpid() && strcmp(s_list[i]->name, name))
{
return EEXIST;
}
}
//make sure there are no more than 64 semaphores active
if (num_semaphores >= 64)
{
return ENOMEM;
}
//create a new semaphore and add it to the collection
new_s = (named_semaphore*) malloc(sizeof(named_semaphore), 0, 0);
new_s->name = name;
new_s->process = getpid();
new_s->p_process = getppid();
new_s->count = initial_count;
s_list[num_semaphores] = new_s;
++num_semaphores;
//initialize the waiting queue
SIMPLEQ_INIT( &(new_s->head) );
//initialize its lock
simple_lock_init( new_s->slock );
//need to handle negative initial_count somehow
return (0);
}
int down_semaphore( const char* name )
{
named_semaphore* s;
s = getSemaphore( name );
if (s == NULL)
{
return (ENOENT);
}
s->count = (s->count) - 1;
if (s->count < 0)
{
//put process to sleep
tsleep(getpid(), getpriority(), 0, 0);
//add process to waiting queue
np = (struct entry *) malloc(sizeof(struct entry ));
np->id = getpid();
SIMPLEQ_INSERT_TAIL( &(s->head), np, next );
}
return 0;
}
int up_semaphore ( const char* name )
{
named_semaphore* s;
s = getSemaphore( name );
if ( s == NULL )
{
return (ENOENT);
}
s->count = (s->count) + 1;
if (s->count <= 0)
{
//wakeup longest waiting process
wakeup( (SIMPLEQ_FIRST( &(s->head) ))->id );
//remove process from waiting queue
SIMPLEQ_REMOVE_HEAD( &(s->head), np, next );
free( np );
}
return 0;
}
int free_semaphore( const char* name )
{
named_semaphore* s;
s = getSemaphore( name );
if ( s == NULL )
{
return (ENOENT);
}
simple_lock( s->slock );
while ( (np = SIMPLEQ_FIRST( &(s->head) ) ) != NULL )
{
//wakeup the process and return ECONNABORTED
//wakeup( getSemaphore( np->id ) );
SIMPLEQ_REMOVE_HEAD( &(s->head), np, next );
free( np );
}
free( s );
simple_unlock( s->slock );
}
I am not done modifying/fixing the logic of my overall program (for example, the lock()ing only happens in 1/3 of the intended methods), but it would be wonderful to understand why I am getting my current error so that I know how to fix similar ones in the future.
To me it seems like the methods do not recognize their header files or that I am missing a required piece of information so that the two can communicate.
To fix the errors, I've tried rearranging and commenting out the listed header files and also renaming the method calls in uppercase letters like they were presented in the header file documentation.
Any help or insight is appreciated, and thank you in advance!
The man pages you read... those were section 9, weren't they? Section 9 is for kernel programming. You can't call those functions unless your code is in the kernel.

OSX FSEventStreamEventFlags not working correctly

I am watching a directory for file system events. Everything seems to work fine with one exception. When I create a file the first time, it spits out that it was created. Then I can remove it and it says it was removed. When I go to create the same file again, I get both a created and removed flag at the same time. I obviously am misunderstanding how the flags are being set when the callback is being called. What is happening here?
//
// main.c
// GoFSEvents
//
// Created by Kyle Cook on 8/22/13.
// Copyright (c) 2013 Kyle Cook. All rights reserved.
//
#include <CoreServices/CoreServices.h>
#include <stdio.h>
#include <string.h>
void eventCallback(FSEventStreamRef stream, void* callbackInfo, size_t numEvents, void* paths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]) {
char **pathsList = paths;
for(int i = 0; i<numEvents; i++) {
uint32 flag = eventFlags[i];
uint32 created = kFSEventStreamEventFlagItemCreated;
uint32 removed = kFSEventStreamEventFlagItemRemoved;
if(flag & removed) {
printf("Item Removed: %s\n", pathsList[i]);
}
else if(flag & created) {
printf("Item Created: %s\n", pathsList[i]);
}
}
}
int main(int argc, const char * argv[])
{
CFStringRef mypath = CFSTR("/path/to/dir");
CFArrayRef paths = CFArrayCreate(NULL, (const void **)&mypath, 1, NULL);
CFRunLoopRef loop = CFRunLoopGetMain();
FSEventStreamRef stream = FSEventStreamCreate(NULL, (FSEventStreamCallback)eventCallback, NULL, paths, kFSEventStreamEventIdSinceNow, 1.0, kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagNoDefer);
FSEventStreamScheduleWithRunLoop(stream, loop, kCFRunLoopDefaultMode);
FSEventStreamStart(stream);
CFRunLoopRun();
FSEventStreamStop(stream);
FSEventStreamInvalidate(stream);
FSEventStreamRelease(stream);
return 0;
}
As far as I can tell, you will have to look for either kFSEventStreamEventFlagItemRemoved or kFSEventStreamEventFlagItemCreated, and then use stat() or similar to check if the file was in fact added or deleted. The FSEvents documentation seems to hint as such.
It looks like the API is or'ing the events bits together... so really it's an OR of all the changes made since the FSEventsListener is created. Since that seems to be the case, another option might be to create a new FSEventListener each time (and use the coalesce timer option).
I did some Googling, but didn't find other examples of this problem or even apple sample code, but I didn't spend too long on it.
I have previously used the kqueue API: https://gist.github.com/nielsbot/5155671 (This gist is an obj-c wrapper around kqueue)
I changed your sample code to show all flags set for each FSEvent:
#include <CoreServices/CoreServices.h>
#include <stdio.h>
#include <string.h>
static int __count = 0 ;
void eventCallback(FSEventStreamRef stream, void* callbackInfo, size_t numEvents, void* paths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]) {
char **pathsList = paths;
printf("callback #%u\n", ++__count ) ;
const char * flags[] = {
"MustScanSubDirs",
"UserDropped",
"KernelDropped",
"EventIdsWrapped",
"HistoryDone",
"RootChanged",
"Mount",
"Unmount",
"ItemCreated",
"ItemRemoved",
"ItemInodeMetaMod",
"ItemRenamed",
"ItemModified",
"ItemFinderInfoMod",
"ItemChangeOwner",
"ItemXattrMod",
"ItemIsFile",
"ItemIsDir",
"ItemIsSymlink",
"OwnEvent"
} ;
for(int i = 0; i<numEvents; i++)
{
printf("%u\n", i ) ;
printf("\tpath %s\n", pathsList[i]) ;
printf("\tflags: ") ;
long bit = 1 ;
for( int index=0, count = sizeof( flags ) / sizeof( flags[0]); index < count; ++index )
{
if ( ( eventFlags[i] & bit ) != 0 )
{
printf("%s ", flags[ index ] ) ;
}
bit <<= 1 ;
}
printf("\n") ;
}
FSEventStreamFlushSync( stream ) ;
}
int main(int argc, const char * argv[])
{
CFStringRef path = CFStringCreateWithCString( kCFAllocatorDefault, argv[1], kCFStringEncodingUTF8 ) ;
CFArrayRef paths = CFArrayCreate(NULL, (const void **)&path, 1, &kCFTypeArrayCallBacks );
if ( path ) { CFRelease( path ) ; }
CFRunLoopRef loop = CFRunLoopGetCurrent() ;
FSEventStreamRef stream = FSEventStreamCreate(NULL, (FSEventStreamCallback)eventCallback, NULL, paths, kFSEventStreamEventIdSinceNow, 0, kFSEventStreamCreateFlagFileEvents );
if ( paths ) { CFRelease( paths ) ; }
FSEventStreamScheduleWithRunLoop(stream, loop, kCFRunLoopDefaultMode);
FSEventStreamStart(stream);
CFRunLoopRun() ;
FSEventStreamStop(stream);
FSEventStreamInvalidate(stream);
FSEventStreamRelease(stream);
return 0;
}

Resources