3

The scenario is like this: "I run an app (say myproc) from one user and then fast user switch to second user"
Now, when I try to determine all processes running with a particular bundle Identifier (say com.ak.myproc); I am not able to determine this for processes running from first user.

I've tried the following but in vain:

  1. [NSRunningApplication runningApplicationsWithBundleIdentifier:]
  2. [[NSWorkspace sharedWorkspace] runningApplications] and then comparing bundle identifier of each application - the app running for first user does not even show up in this list.
  3. using sysctl() and then iterating through the process list - Here, the pid of the app from first user does come. After that:
    • When I try [NSRunningApplication runningApplicationWithProcessIdentifier:], I get nil.
    • When I try GetProcessForPID() followed by ProcessInformationCopyDictionary(), I get a nil dictionary.
    • When I try GetProcessForPID() followed by GetProcessInformation(), I do not get anything useful in ProcessInfoRec.

Can somebody please help? Thanks.

OS: Mac OS X 10.8.4
Xcode: 4.6.2

dDarkLORD
  • 624
  • 7
  • 25
  • Also [here](http://stackoverflow.com/questions/18820199/unable-to-detect-application-running-with-another-user-via-switch-user?rq=1) is a reference from a similar question. – dDarkLORD Nov 08 '13 at 09:55

1 Answers1

5

You can map process name to bundle id using NSWorkspace.

#include <sys/sysctl.h>
#include <pwd.h>
typedef struct kinfo_proc kinfo_proc;
static int GetBSDProcessList(kinfo_proc **procList, size_t *procCount)
// Returns a list of all BSD processes on the system.  This routine
// allocates the list and puts it in *procList and a count of the
// number of entries in *procCount.  You are responsible for freeing
// this list (use "free" from System framework).
// On success, the function returns 0.
// On error, the function returns a BSD errno value.
{
    int                 err;
    kinfo_proc *        result;
    bool                done;
    static const int    name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
    // Declaring name as const requires us to cast it when passing it to
    // sysctl because the prototype doesn't include the const modifier.
    size_t              length;

    //    assert( procList != NULL);
    //    assert(*procList == NULL);
    //    assert(procCount != NULL);

    *procCount = 0;

    // We start by calling sysctl with result == NULL and length == 0.
    // That will succeed, and set length to the appropriate length.
    // We then allocate a buffer of that size and call sysctl again
    // with that buffer.  If that succeeds, we're done.  If that fails
    // with ENOMEM, we have to throw away our buffer and loop.  Note
    // that the loop causes use to call sysctl with NULL again; this
    // is necessary because the ENOMEM failure case sets length to
    // the amount of data returned, not the amount of data that
    // could have been returned.

    result = NULL;
    done = false;
    do {
        assert(result == NULL);

        // Call sysctl with a NULL buffer.

        length = 0;
        err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
                     NULL, &length,
                     NULL, 0);
        if (err == -1) {
            err = errno;
        }

        // Allocate an appropriately sized buffer based on the results
        // from the previous call.

        if (err == 0) {
            result = malloc(length);
            if (result == NULL) {
                err = ENOMEM;
            }
        }

        // Call sysctl again with the new buffer.  If we get an ENOMEM
        // error, toss away our buffer and start again.

        if (err == 0) {
            err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
                         result, &length,
                         NULL, 0);
            if (err == -1) {
                err = errno;
            }
            if (err == 0) {
                done = true;
            } else if (err == ENOMEM) {
                assert(result != NULL);
                free(result);
                result = NULL;
                err = 0;
            }
        }
    } while (err == 0 && ! done);

    // Clean up and establish post conditions.

    if (err != 0 && result != NULL) {
        free(result);
        result = NULL;
    }
    *procList = result;
    if (err == 0) {
        *procCount = length / sizeof(kinfo_proc);
    }

    assert( (err == 0) == (*procList != NULL) );

    return err;
}

+ (NSArray*)getBSDProcessList
{
    kinfo_proc *mylist =NULL;
    size_t mycount = 0;
    GetBSDProcessList(&mylist, &mycount);

    NSMutableArray *processes = [NSMutableArray arrayWithCapacity:(int)mycount];

    for (int i = 0; i < mycount; i++) {
        struct kinfo_proc *currentProcess = &mylist[i];
        struct passwd *user = getpwuid(currentProcess->kp_eproc.e_ucred.cr_uid);
        NSMutableDictionary *entry = [NSMutableDictionary dictionaryWithCapacity:4];

        NSNumber *processID = [NSNumber numberWithInt:currentProcess->kp_proc.p_pid];
        NSString *processName = [NSString stringWithFormat: @"%s",currentProcess->kp_proc.p_comm];
        if (processID)[entry setObject:processID forKey:@"processID"];
        if (processName)[entry setObject:processName forKey:@"processName"];
        if (processName)
        {
            NSString *bunldeID = [self bundleIdentifierForApplicationName:processName];
            if (bunldeID)
                [entry setObject:bunldeID forKey:@"bundleId"];
        }
        if (user){
            NSNumber *userID = [NSNumber numberWithUnsignedInt:currentProcess->kp_eproc.e_ucred.cr_uid];
            NSString *userName = [NSString stringWithFormat: @"%s",user->pw_name];

            if (userID)[entry setObject:userID forKey:@"userID"];
            if (userName)[entry setObject:userName forKey:@"userName"];
        }
        [processes addObject:[NSDictionary dictionaryWithDictionary:entry]];
    }
    free(mylist);

    return [NSArray arrayWithArray:processes];
}
+ (NSString *) bundleIdentifierForApplicationName:(NSString *)appName
{
    NSWorkspace * workspace = [NSWorkspace sharedWorkspace];
    NSString * appPath = [workspace fullPathForApplication:appName];
    if (appPath) {
        NSBundle * appBundle = [NSBundle bundleWithPath:appPath];
        return [appBundle bundleIdentifier];
    }
    return nil;
}
Parag Bafna
  • 22,812
  • 8
  • 71
  • 144
  • Thanks @Parag, it is working for many apps like Google Chrome, Wireshark, Perforce and Apple apps but it does not work for some like Adobe Reader. Why may this be happening? – dDarkLORD Nov 18 '13 at 12:29
  • check info.plist file of Adobe Reader – Parag Bafna Nov 18 '13 at 13:21
  • 1
    I compared the info.plist of Safari as well as Adobe Reader. They seemed pretty much similar. They were two extra keys in Reader's plist i.e "Application requires Carbon environment" and "Application requires Native environment". I removed them but still the same result. Anyway, I've opened a Support case for the same - will update once I get the answer. Thanks. – dDarkLORD Nov 19 '13 at 11:51