Programmatically check if a process is running on Mac
Asked Answered
M

6

12

Is there any Carbon/Cocoa/C API available on Macs that I can use to enumerate processes? I'm looking for something like EnumProcesses on Windows.

My goal is to check from code whether a process is running (by name).

Thanks!

Mishap answered 25/3, 2010 at 18:1 Comment(0)
M
10

TechZen says: The Process Manager is, as of Dec 2013, completely deprecated.

Ah, I just found the Process Manager reference

Looks like GetNextProcess and GetProcessInfo help in figuring out what's running. As suggested by Dave, GetBSDProcessList can be used if you're looking for daemons and not just Carbon/Cocoa processes.

Mishap answered 25/3, 2010 at 18:15 Comment(8)
You may not want to use these older functions as Apple may choose to depreciate them. Cocoa classes are generally safer in this respect.Byzantium
FYI, GetBSDProcessList is much faster than iterating through the Process Manager yourself.Domestic
The Process Manager is not (currently) deprecated and is available in 64-bit. I don't think it has an axe over its head the way some of the other APIs have.Clearly
This does seem to be best documented of all the options above, and I don't see any notes regarding deprecation. For a one-time check of running processes from any Carbon/Cocoa app (not just Dock apps), this seems ideal even if it might be slower than other options.Mishap
no deprecation yet, runningApplications being 10.6 only these look greatMacedonia
The Process Manager is as of Dec 2013 complete depreciated.Braeunig
excuse me guys, which framework/library is GetBSDProcessList in? my Xcode 7 doesn't know anything about this function/method, neither in docs nor in quick-open, so...Goforth
The link for the Process manager reference is broken. I'm new to Apple development, but it seems to be a normal thing for them.Midden
M
18

Here are some specific implementations and details, note that proc->kp_proc.p_comm has a character length limit that's why I'm implemented infoForPID: instead

Cocoa :

[NSWorkspace launchedApplications] (10.2+ , deprecated in 10.7, very limited process listing) [NSWorkspace runningApplications] (10.6+ , less limited process listing but still not including daemon processes)

Carbon :

- (NSArray*)getCarbonProcessList
{
    NSMutableArray *ret = [NSMutableArray arrayWithCapacity:1];
    ProcessSerialNumber psn = { kNoProcess, kNoProcess };
    while (GetNextProcess(&psn) == noErr) {
        CFDictionaryRef cfDict = ProcessInformationCopyDictionary(&psn,  kProcessDictionaryIncludeAllInformationMask);
        if (cfDict) {
            NSDictionary *dict = (NSDictionary *)cfDict;
            [ret addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                            [NSString stringWithFormat:@"%@",[dict objectForKey:(id)kCFBundleNameKey]],@"pname",
                            [NSString stringWithFormat:@"%@",[dict objectForKey:@"pid"]],@"pid",
                            [NSString stringWithFormat:@"%d",(uid_t)getuid()],@"uid",                                               
                            nil]]; 
            CFRelease(cfDict);          
        }
    }
    return ret;
}

C: (see Technical Q&A QA1123 Getting List of All Processes on Mac OS X )

- (NSArray*)getBSDProcessList
{
    NSMutableArray *ret = [NSMutableArray arrayWithCapacity:1];
    kinfo_proc *mylist;
    size_t mycount = 0;
    mylist = (kinfo_proc *)malloc(sizeof(kinfo_proc));
    GetBSDProcessList(&mylist, &mycount);
    int k;
    for(k = 0; k < mycount; k++) {
        kinfo_proc *proc = NULL;
        proc = &mylist[k];
        NSString *fullName = [[self infoForPID:proc->kp_proc.p_pid] objectForKey:(id)kCFBundleNameKey];
        if (fullName == nil) fullName = [NSString stringWithFormat:@"%s",proc->kp_proc.p_comm];
        [ret addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                        fullName,@"pname",
                        [NSString stringWithFormat:@"%d",proc->kp_proc.p_pid],@"pid",
                        [NSString stringWithFormat:@"%d",proc->kp_eproc.e_ucred.cr_uid],@"uid",                                               
                        nil]];                                            
    }
    free(mylist);  
    return ret;
}

- (NSDictionary *)infoForPID:(pid_t)pid 
{
    NSDictionary *ret = nil;
    ProcessSerialNumber psn = { kNoProcess, kNoProcess };
    if (GetProcessForPID(pid, &psn) == noErr) {
        CFDictionaryRef cfDict = ProcessInformationCopyDictionary(&psn,kProcessDictionaryIncludeAllInformationMask); 
        ret = [NSDictionary dictionaryWithDictionary:(NSDictionary *)cfDict];
        CFRelease(cfDict);
    }
    return ret;
}
Macedonia answered 22/9, 2010 at 18:44 Comment(8)
this is equivalent to use NSArray *runningApp=[[NSWorkspace sharedWorkspace]runningApplications]; and doesnt work to get the list of Daemon processes running.Wobble
@Wobble yes it is equivalent to runningApplications but it is significantly more complete than the list from launchedApplications, the C approach gives you the daemons tooMacedonia
can anyone explain how can we get the memory usage of these process?Senegambia
First, great answer. complete and detailed and enlightening. Second, It aught to be said that GetBSDProcessList (relied on) is a sample code taken from Apple's TechNote. Last - there's a memory issue in that implementation from Apple, and a potential leak in the code here that uses it. Apple's implementation for some reason requires that '' assert(*procList == NULL);'' which fails immediately with this code. I don't know how is it should be!Goforth
The infoForPID: implementation only helps with Cocoa/Carbon bundled applications (doesn't cover the vast majority of daemons). daemons have their names cut to 16 bytes, despite the "fullname" naming of the NSString. Any ideas how to get the FULL name?Goforth
@Macedonia you get memory leak with: mylist = (kinfo_proc *)malloc(sizeof(kinfo_proc)); . It should be corrected to mylist = NULL.Knowitall
@Motti Shneor You may try proc_name function in <libproc.h>. Usage: proc_name(pid, procname, 128)Knowitall
That name is also truncated and in any-way not aligned with the results of 'ps' or 'Activity monitor'. I tried it. Last, I wrote something with this logic: If the process is launched from an NSBundle - I read the name from the bundle. otherwise, I take the "last path component" of the executable path. That kind'a does the trick.Goforth
M
10

TechZen says: The Process Manager is, as of Dec 2013, completely deprecated.

Ah, I just found the Process Manager reference

Looks like GetNextProcess and GetProcessInfo help in figuring out what's running. As suggested by Dave, GetBSDProcessList can be used if you're looking for daemons and not just Carbon/Cocoa processes.

Mishap answered 25/3, 2010 at 18:15 Comment(8)
You may not want to use these older functions as Apple may choose to depreciate them. Cocoa classes are generally safer in this respect.Byzantium
FYI, GetBSDProcessList is much faster than iterating through the Process Manager yourself.Domestic
The Process Manager is not (currently) deprecated and is available in 64-bit. I don't think it has an axe over its head the way some of the other APIs have.Clearly
This does seem to be best documented of all the options above, and I don't see any notes regarding deprecation. For a one-time check of running processes from any Carbon/Cocoa app (not just Dock apps), this seems ideal even if it might be slower than other options.Mishap
no deprecation yet, runningApplications being 10.6 only these look greatMacedonia
The Process Manager is as of Dec 2013 complete depreciated.Braeunig
excuse me guys, which framework/library is GetBSDProcessList in? my Xcode 7 doesn't know anything about this function/method, neither in docs nor in quick-open, so...Goforth
The link for the Process manager reference is broken. I'm new to Apple development, but it seems to be a normal thing for them.Midden
D
8

There are a couple ways you can do this:

  1. If it's a GUI app with a Dock icon, use -[NSWorkspace launchedApplications].
  2. Fork off another process (like ps or top or whatever) via an NSTask, read the results, and search yourself (or pipe it through grep or something).
  3. Use the GetBSDProcessList function described here: http://developer.apple.com/legacy/mac/library/#qa/qa2001/qa1123.html (I've used this successfully in the past)
Domestic answered 25/3, 2010 at 18:7 Comment(1)
Hi Dave, Any chance you could dig up an updated link? Seems that Apple has rearranged their developer site, the link no longer works. Thanks!Ironclad
W
4

Late to the party, but if you really need a robust solution that can check whether any process is running (including BSD processes), you can do the following:


#include <stdlib.h>
#include <string.h>
#include <stdio.h>

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

int main(int argc, const char* argv[]) {

  pid_t pid = atoi(argv[2]);  

  // This MIB array will get passed to sysctl()
  // See man 3 sysctl for details
  int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid };

  struct kinfo_proc result;
  size_t oldp_len = sizeof(result);

  // sysctl() refuses to fill the buffer if the PID does not exist,
  // so the only way to detect failure is to set all fields to 0 upfront
  memset(&result, 0, sizeof(struct kinfo_proc));

  if (sysctl(name, 4, &result, &oldp_len, NULL, 0) < 0) { 
    perror("sysctl");
    return 1;
  }

  // SZOMB means a zombie process, one that is still visible but is not running anymore
  if (result.kp_proc.p_pid > 0 && result.kp_proc.p_stat != SZOMB) {
    printf("Process is running.\n");
  } else {
    printf("Process is NOT running.\n");
  }

  return 0;

}

Note that the above code is a modified version of one of my private libraries and is untested. However, it should make clear how the API is used, and works successfully on macOS 10.14.5.

Whipsaw answered 29/7, 2019 at 9:23 Comment(3)
I think you have a point. However, I used this to create a simple generic task manager. If you really need to list all processes currently running on the system (like a task manager requires), it is (or was) one of the few options available.Whipsaw
Sorry, I deleted my comment as it was applied to the wrong answer --- my words were for the guy above who suggested using the new EndpointSecurity framework... Yours (looks like some variation of the code you'll find in the implementation of 'ps' ) is reasonable.Goforth
Works flawlessly on Big SurInpatient
B
3

In the overview of the NSRunningApplicationClass, it says:

NSRunningApplication is a class to manipulate and provide information for a single instance of an application. Only user applications are tracked; this does not provide information about every process on the system.

and

To access the list of all running applications, use the runningApplications method in NSWorkspace.

I would suggest taking a look at Workspace Services Programming Topics

Byzantium answered 25/3, 2010 at 18:16 Comment(1)
Those are great, if you can limit yourself to GUI apps on 10.6Domestic
K
0

You might use EndPointSecurity.framework which is available since macOS 10.15. Refer to Writing a Process Monitor with Apple's Endpoint Security Framework for more information

Knowitall answered 27/11, 2020 at 10:14 Comment(1)
While that can finally work, that's like killing an ant with a sledge-hammer, in simple words: Overkill. First off, you need your "process monitor" alive at all times since very early stages of boot, and also keep it running at all times, to have a complete and dependable list at all times. Then - gaining the entitlement from Apple isn't easy either. – Motti Shneor Nov 28 at 19:20 DeleteGoforth

© 2022 - 2024 — McMap. All rights reserved.