4

Given a pid, I want to find the owner of the process (as uid). Is there a way to get this in osx (or any unix) using C++?

Google didn't help. 'ps' is able to do it; so I assume there should be a way to get it programatically.

Indhu Bharathi
  • 1,437
  • 1
  • 13
  • 22

5 Answers5

5

Solution from Indhu helped me on my way, so I would like to post my own.

UID from PID with pure C:

#include <sys/sysctl.h>

uid_t uidFromPid(pid_t pid)
{
    uid_t uid = -1;

    struct kinfo_proc process;
    size_t procBufferSize = sizeof(process);

    // Compose search path for sysctl. Here you can specify PID directly.
    const u_int pathLenth = 4;
    int path[pathLenth] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};

    int sysctlResult = sysctl(path, pathLenth, &process, &procBufferSize, NULL, 0);

    // If sysctl did not fail and process with PID available - take UID.
    if ((sysctlResult == 0) && (procBufferSize != 0))
    {
        uid = process.kp_eproc.e_ucred.cr_uid;
    }

    return uid;
}

No excess allocation, no loops.

Al Zonke
  • 532
  • 6
  • 8
  • I know it's been awhile, but I'm curious if anyone else ran into the same thing? For some PIDs (for a process that is still running) the call to `sysctl` returns success (or 0) but it sets the result in its 4th parameter (or your `procBufferSize`) to 0. What could be causing it? – c00000fd Apr 24 '23 at 08:27
3

The source for the ps command, reveals that there is a function called get_proc_stats defined in proc/readproc.h that (among other things) returns the real user name(UID) & Effective user name(EUID) for a given pid.

You need to do install libproc-dev to get this function. and then you can do:

#include <proc/readproc.h>
void printppid(pid_t pid) 
{
    proc_t process_info;
    get_proc_stats(pid, &process_info);
    printf("Real user of the process[%d] is [%s]\n", pid, process_info.ruser);
}

compile it with gcc the-file.c -lproc.

Once you have the real user name you can use getpwnam() and getgrnam() functions to get the uid.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • Where do I get libproc from? I tried building it from 'http://www.opensource.apple.com/source/dtrace/dtrace-78/libproc/'. But, it seems to be using some private headers like '#include ' – Indhu Bharathi Jun 30 '11 at 05:04
2

You could look at how ps does it. It looks like it uses the kvm_getprocs function.

However, it's much more portable (you said "any unix", but e.g. the Linux and Solaris way is to look in the /proc filesystem - and other unixes may have different APIs) to just parse the output of ps (ps -o user= -p (pid) for example, to eliminate any extraneous output) than to do any system-specific process stuff

Random832
  • 37,415
  • 3
  • 44
  • 63
  • Looks like Apple is phasing out the kvm interface. http://www.clusterresources.com/pipermail/torquedev/2006-December/000395.html – Indhu Bharathi Jun 30 '11 at 04:49
2

There's not a portable way to do this. On Mac OS, you've got to use poorly documented sysctl interfaces: see this previous stackoverflow question. (As other commenters pointed out, on Linux you can use proc. On FreeBSD, you should be able to use kvm_getfiles, although this is not available on Mac OS.)

Your best bet is to use the source for Apple's ps as a jumping-off point for grabbing process data and then you'll be able to use getpwuid(3) once you have the uid.

Community
  • 1
  • 1
Edward Thomson
  • 74,857
  • 14
  • 158
  • 187
1

Finally found a way to programatically do this without parsing the output of 'ps'

uint getUidUsingSysctl(uint pid)
{
    struct kinfo_proc *sProcesses = NULL, *sNewProcesses;
    int    aiNames[4];
    size_t iNamesLength;
    int    i, iRetCode, iNumProcs;
    size_t iSize;

    iSize = 0;
    aiNames[0] = CTL_KERN;
    aiNames[1] = KERN_PROC;
    aiNames[2] = KERN_PROC_ALL;
    aiNames[3] = 0;
    iNamesLength = 3;

    iRetCode = sysctl(aiNames, iNamesLength, NULL, &iSize, NULL, 0);

    /* allocate memory and populate info in the  processes structure */
    do
    {
        iSize += iSize / 10;
        sNewProcesses = (kinfo_proc *)realloc(sProcesses, iSize);

        if (sNewProcesses == 0)
        {
            if (sProcesses)
                free(sProcesses);
            /* could not realloc memory, just return */
            return -1;
        }
        sProcesses = sNewProcesses;
        iRetCode = sysctl(aiNames, iNamesLength, sProcesses, &iSize, NULL, 0);
    } while (iRetCode == -1 && errno == ENOMEM);

    iNumProcs = iSize / sizeof(struct kinfo_proc);

    for (i = 0; i < iNumProcs; i++)
    {
        if (sProcesses[i].kp_proc.p_pid == pid) 
        {
            return sProcesses[i].kp_eproc.e_ucred.cr_uid;
        }

    }

    /* clean up and return to the caller */
    free(sProcesses);

    return -1;
}

Note: There might be a better way to get 'kinfo_proc' instead of iterating through all process.

Indhu Bharathi
  • 1,437
  • 1
  • 13
  • 22