Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I capture a list of all running processes for OSX and save them as a file in Xcode / Cocoa?

Tags:

xcode

macos

cocoa

I am wanting to capture a list of all running processes for OSX and save them as a file in Xcode / Cocoa. I googled this and all I found was:

[myWorkspace runningApplications];

And I am not sure how to do this. Please Help! Thank you!

like image 221
user1672371 Avatar asked Sep 14 '12 20:09

user1672371


People also ask

How do I list all processes on a Mac?

Launch Terminal (Finder > Applications > Utilities). When Terminal is running, type top and hit Return. This will pull up a list of all your currently running processes.

How do I see what services are running on my Mac?

On your Mac, choose Apple menu > System Preferences, click Keyboard , then click Shortcuts. In the sidebar, click Services, then select items to make them available in the Services menu or deselect items to remove them. Items appear in an app's Services menu only if they're relevant to the app or task.


2 Answers

yip to get high-level query the workspace as shown by abarnet to get 'all' processes go lower-level and execute ps via an NSTask and read the response via a NSPipe.

there's sample code by apple .. or use DDTask (a wrapper I wrote) github repository

NSString *exe = @"/bin/ps";
NSArray *args = @[@"-ax"];
NSString *res = [DDTask runTaskWithToolPath:exe andArguments:args andErrorHandler:nil];

[res writeToFile:@"./appslist" 
      atomically:YES
        encoding:NSUTF8StringEncoding
           error:NULL];
like image 41
Daij-Djan Avatar answered Oct 17 '22 01:10

Daij-Djan


As explained in QA1123, "process" means multiple different things on a Mac (and that changes over time).

A "process" at the level of Cocoa (or Carbon, or once upon a time Classic) is basically what an end-user thinks of as a process: an app, fba, launchitem, etc. A "process" at the level of BSD is what a Unix-trained sysadmin thinks of as a process: something that shows up in ps. A high-level process can have multiple BSD processes; the other way around used to be possible too (under Classic); you can also have BSD processes that have no high-level process; etc.

If you want the high-level definition, forget that QA, the method -[NSWorkspace runningApplications] that you mentioned returns exactly what you want: an array with an object for each such app, and those objects have all the info you want. How you save them in a file depends on what information you want about each one, what format you want to save that information in, etc. Here's a complete sample app that will save the URL of each app, one per line, to a file called "./appslist":

#include <Cocoa/Cocoa.h>

int main(int argc, char *argv[]) {
  NSString *output = [NSString string];
  for (NSRunningApplication *app in 
       [[NSWorkspace sharedWorkspace] runningApplications]) {
    output = [output stringByAppendingFormat:@"%@\n",
                     [[app bundleURL] absoluteString]];
  }
  [output writeToFile:@"./appslist" 
           atomically:YES
             encoding:NSUTF8StringEncoding
                error:NULL];
  return 0;
}

If you want the low-level definition, the code in that QA is still accurate. Or you could just exec (or system or NSTask) ps.

Anyway, here's a sample that (with the code from that QA) prints the pid of each running process to a local file called "./bsdlist":

int main(int argc, char *argv[]) {
  kinfo_proc *procs;
  size_t count;
  int err = GetBSDProcessList(&procs, &count);
  if (err) return err;
  FILE *f = fopen("./bsdlist", "w");
  for (size_t i=0; i!=count; ++i) {
    fprintf(f, "%d\n", procs[i].kp_proc.p_pid);
  }
  fclose(f);
  free(procs);
}

If you like the idea of scripting ps, as mentioned above, there are a number of ways to do this.

The DDTask library mentioned in Dominik's answer looks like the easiest way to do this. If you want to use NSTask directly, it takes a bit more code to set up an NSPipe for stdout, etc. There's a good general CocoaDev (it's scripting ls rather than ps, but the ideas are all the same.)

Or you could drop down to a lower level. You could explicitly fork/exec and pass the results through stdout, but the popen function is designed to wrap all of that up, and it's a lot easier to use. The GNU C Programming Tutorial shows how to popen ps -A and pipe it to grep init; the first half of this is all you need.

Whichever way you go, the problem is that you're going to get back a mess of strings you have to parse. The best thing to do is pass different flags to ps to only get what you actually want. If all you want is the command lines, ps -ax -ocommand= will give you nothing but that, one command line per line—no header line to skip, no columns to parse apart, etc.

If you're worried about efficiency: The QA says "exec'ing ps will require parsing the tool's output and will not use system resources as efficiently as Listing 1." And this is true; formatting the sysctl output into strings just to pass them over a pipe and parse them again is extra work that has to take some CPU time. But unless you're doing this millions of times, I doubt it uses enough to make a difference. The best approach (to this, and most cases) is to write the simpler code first and test whether it's fast enough; if it is, you're done.

like image 94
abarnert Avatar answered Oct 16 '22 23:10

abarnert