35

First off, I know that similar questions have been asked, but the answers provided haven't been very helpful so far (they all recommend one of the following options).

I have a user application that needs to determine if a particular process is running. Here's what I know about the process:

  • The name
  • The user (root)
  • It should already be running, since it's a LaunchDaemon, which means
  • Its parent process should be launchd (pid 1)

I've tried several ways to get this, but none have worked so far. Here's what I've tried:

  1. Running ps and parsing the output. This works, but it's slow (fork/exec is expensive), and I'd like this to be as fast as possible.

  2. Using the GetBSDProcessList function listed here. This also works, but the way in which they say to retrieve the process name (accessing kp_proc.p_comm from each kinfo_proc structure) is flawed. The resulting char* only contains the first 16 characters of the process name, which can be seen in the definition of the kp_proc structure:

    #define MAXCOMLEN 16 //defined in param.h
    struct extern_proc {  //defined in proc.h
      ...snip...
      char p_comm[MAXCOMLEN+1];
      ...snip...
    };
  3. Using libProc.h to retrieve process information:

    pid_t pids[1024];
    int numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);   
    proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(pids));    
    for (int i = 0; i < numberOfProcesses; ++i) {
      if (pids[i] == 0) { continue; }
      char name[1024];
      proc_name(pids[i], name, sizeof(name));
      printf("Found process: %s\n", name);
    }

    This works, except it has the same flaw as GetBSDProcessList. Only the first portion of the process name is returned.

  4. Using the ProcessManager function in Carbon:

    ProcessSerialNumber psn;
    psn.lowLongOfPSN = kNoProcess;
    psn.highLongOfPSN = 0;
    while (GetNextProcess(&psn) == noErr) {
      CFStringRef procName = NULL;
      if (CopyProcessName(&psn, &procName) == noErr) {
        NSLog(@"Found process: %@", (NSString *)procName);
      }
      CFRelease(procName);
    }

    This does not work. It only returns process that are registered with the WindowServer (or something like that). In other words, it only returns apps with UIs, and only for the current user.

  5. I can't use -[NSWorkspace launchedApplications], since this must be 10.5-compatible. In addition, this only returns information about applications that appear in the Dock for the current user.

I know that it's possible to retrieve the name of running processes (since ps can do it), but the question is "Can I do it without forking and exec'ing ps?".

Any suggestions?

EDIT

After doing a lot more research, I've been unable to find a way to do this. I found this SO question, which referred to this C file in a python module. This was really useful in trying to use the KERN_PROCARGS values in a sysctl call.

However, Python module code seemed to be derived from the source to ps, which I found here. ps can somehow get the executable path of every running process, but my best efforts to extract how its doing this have been unsuccessful. There's a function in print.c called getproclline that seems to be doing the magic, but when I run the same code from within my own command line tool, I'm unable to retrieve the process executable for any processes other than my own.

I'll keep experimenting, but without more conclusive evidence, it looks like @drawnonward's answer is the most correct so far.


EDIT (a long time later)

Thanks to the answer pointed to by Quinn Taylor, I've found something that works. It gets the executable path of each process, and then I can just grab the last path component to get the actual process name.

#import <sys/proc_info.h>
#import <libproc.h>

int numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
pid_t pids[numberOfProcesses];
bzero(pids, sizeof(pids));
proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(pids));
for (int i = 0; i < numberOfProcesses; ++i) {
    if (pids[i] == 0) { continue; }
    char pathBuffer[PROC_PIDPATHINFO_MAXSIZE];
    bzero(pathBuffer, PROC_PIDPATHINFO_MAXSIZE);
    proc_pidpath(pids[i], pathBuffer, sizeof(pathBuffer));
    if (strlen(pathBuffer) > 0) {
        printf("path: %s\n", pathBuffer);
    }
}
Community
  • 1
  • 1
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • I seem to recall a `sysctl()` call that returns a complete list, though the process names were truncated at a certain length. – Wevah Jun 10 '10 at 22:06
  • 1
    @Wevah that's correct. `sysctl()` does return the complete list, but the names are truncated to 16 characters. That's item #2 in the list. :) – Dave DeLong Jun 10 '10 at 22:58
  • Ah, I didn't see `sysctl()` mentioned (I also didn't click the link). (Voting the question up, too, as I could use this info!) – Wevah Jun 10 '10 at 23:01
  • @DaveDeLong I have application with NSUIElement is set to 1, I am getting application name Using the ProcessManager function. – Parag Bafna Mar 02 '12 at 11:14
  • Thanks for sharing your solution @DaveDeLong! Do you know if your solution works in Linux/BSD as well? Or is libproc.h mac only? – Noitidart Oct 22 '16 at 16:26
  • Sorry to raise the dead... but I need the whole command line (meaning the executable path and all the following arguments). KERN_PROCARGS and KERN_PROCARGS2 seem to fill my buffers fine, no need for root, but for some reason I just can't convert the null-punctuated buffer into an array of NSStrings. can anything be said on the encoding of the output of sysctl? in regard to process info ? – Motti Shneor Oct 13 '20 at 20:03
  • 1
    @DaveDeLong I'm using your solution (last path component of the executable path) but feeling uneasy. Why? because in MacOS-X Activity monitor, "process name" is frequently different than the name of the binary executable from which the process was launched. I wonder WHAT IS the process name. – Motti Shneor Oct 19 '20 at 16:23

3 Answers3

9

What about this answer to a related question? https://stackoverflow.com/a/12274588/120292 This purports to get the full path for a process by the pid, and you can grab just the last path component.

Community
  • 1
  • 1
Quinn Taylor
  • 44,553
  • 16
  • 113
  • 131
2

The only complete list of running processes is provided by 2 above, asking the kernel. Getting the actual name of the process is not straight forward. In a nutshell, you look up the pid in any other source you can find until you get a match.

For some processes, the following will work:

ProcessSerialNumber         psn;
CFStringRef             name = NULL;
status = GetProcessForPID( inPID , &psn );
if ( noErr == status ) CopyProcessName( &psn , &name );

For some processes, you can look up the pid in the results of [[NSWorkspace sharedWorkspace] launchedApplications] by NSApplicationProcessIdentifier. Available with 10.2 and later. Most, but maybe not all, items in this list will be the same as CopyProcessName above.

For some processes, you can look up the process arguments and get the full path from the first argument. Similar to getting the original list, but using KERN_PROCARGS or KERN_PROCARGS2 as the second mib value. This is what ps is doing.

For some processes, you are stuck with the 16 character p_comm.

drawnonward
  • 53,459
  • 16
  • 107
  • 112
  • +1 this looks quite promising! Getting the `ProcessSerialNumber` didn't work, but I'll try the `KERN_PROCARGS` approach and report back. – Dave DeLong Jun 10 '10 at 21:06
  • `KERN_PROCARGS` and `KERN_PROCARGS2` work if my app is also running as `root`. Unfortunately, it isn't. :( – Dave DeLong Jun 10 '10 at 21:51
  • 2
    Yes, ps is setuid root. I think there is a special group, maybe procmod, that can also access those. There used to be a whole undocumented CPS suite for this stuff, but it appears to be deprecated. Looks like Apple has made this a security issue. – drawnonward Jun 15 '10 at 03:28
0

Not sure if this is what you're looking for, but could you use the LaunchServices API with __LSCopyApplicationArrayInFrontToBackOrder? I've heard of this, but never used it myself. After some googling, here's a code sample that might provide what you're looking for? I really don't know and am guessing a little ;)

http://gist.github.com/163918

Edit

Actually, Ha. Here's a Stack Overflow post that gives this as an answer and links to the same post I linked to...

http://www.stackoverflow.com/questions/945033/getting-the-list-of-running-applications-ordered-by-last-use

lewiguez
  • 3,813
  • 1
  • 25
  • 40
  • OP wants the name of a non-application process (a unix launch-daemon running as root, with no UI and no window-server registration) so this answer - although very interesting, is nor for this question... – Motti Shneor Oct 19 '20 at 16:32