5

I want to get other process' argv like ps.

I'm using Mac OS X 10.4.11 running on Intel or PowerPC.

First, I read code of ps and man kvm, then I wrote some C code.

#include <kvm.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sysctl.h>
#include <paths.h>

int
main(void) {
    char errbuf[1024];
    kvm_t *kd = kvm_openfiles(_PATH_DEVNULL, NULL, _PATH_DEVNULL, O_RDONLY, errbuf);
    int num_procs;
    if (!kd) { fprintf(stderr, "kvm_openfiles failed : %s\n", errbuf); return 0; }
    struct kinfo_proc *proc_table = kvm_getprocs(kd, KERN_PROC_ALL, 0, &num_procs);

    for (int i = 0; i < num_procs; i++) {
        struct kinfo_proc *pproc = &proc_table[i];
        char **proc_argv = kvm_getargv(kd, pproc, 0);
        printf("%p\n", proc_argv);
    }

    kvm_close(kd);
    return 0;
}

When ran on PowerPC, kvm_getargv() always returned NULL. When ran on Intel, kvm_openfiles() failed with error /dev/mem: No such file or directory.

Of cource, I know about permission.

Second, I tried sysctl.

#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>
#define pid_of(pproc) pproc->kp_proc.p_pid

int
main(void) {

   int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
   int buffer_size;
   sysctl(mib, 4, NULL, &buffer_size, NULL, 0);

   struct kinfo_proc *result = malloc(buffer_size);
   sysctl(mib, 4, result, &buffer_size, NULL, 0);

   int num_procs = buffer_size / sizeof(struct kinfo_proc);
   for (int i = 0; i < num_procs; i++) {
       struct kinfo_proc *pproc = result + i;
       int mib[3] = { CTL_KERN, KERN_PROCARGS, pid_of(pproc) }; // KERN_PROC_ARGS is not defined
       char *proc_argv;
       int argv_len;
       sysctl(mib, 3, NULL, &argv_len, NULL, 0);
       proc_argv = malloc(sizeof(char) * argv_len);
       sysctl(mib, 3, proc_argv, &argv_len, NULL, 0);
       fwrite(proc_argv, sizeof(char), argv_len, stdout);
       printf("\n");
       free(proc_argv);
   }

   return 0;
}

By fwrite, I got argv[0] but argv[1..] are not (environment variables are printed out.)

There is no more way to do it?

SamB
  • 9,039
  • 5
  • 49
  • 56
nonowarn
  • 620
  • 8
  • 18
  • What are you trying to do ? You can attach to the process as a debugger but that's the only valid reason to subvert security. – jim Oct 16 '08 at 21:41
  • I want to treat working processes as data. I see, security is important. but then, why ps can access argv? I also can parse output of ps, but I want whole argv. – nonowarn Oct 16 '08 at 21:56
  • KERN_PROCARGS includes the environment variables in it's output, you need to use KERN_PROCARGS2 which only includes argv. – Jake Dec 05 '22 at 02:49

2 Answers2

7

In 10.6, KERN_PROCARGS2 is available: https://gist.github.com/770696

This way is used from ps, procfs on MacFUSE, etc.

nonowarn
  • 620
  • 8
  • 18
  • really good example of using sysctl. I just want to mention that there's a comment in that file that says "There is no way to deterministically know where the command arguments end and the environment strings start", but it returns the argc so you know to stop processing after you read argc strings. – Jake Dec 05 '22 at 01:31
4

I've actually been needing the same thing for a Python library I'm writing, and in my searching I came across another Python lib (PSI) that implements this in C code. It's part of the python module code for listing processes and includes listing the arguments for each process as well. You could take a look at the source code for that for a working example:

darwin_process.c - scroll down to set_exe() for the relevant code

Note: the site is really slow so you'll have to be a bit patient while it loads.

breakingobstacles
  • 2,815
  • 27
  • 24
Jay
  • 41,768
  • 14
  • 66
  • 83
  • Thanks, Jay. I tried again with browsing and copying your code, I got argv! – nonowarn Oct 18 '08 at 10:13
  • 9
    That link is broken; the file no longer exists. I spent a while browsing around the project's source, and what it boils down to is that they're using 'sysctl'. The current location of that code is in src/arch/darwin_process.c. Hope that helps anyone else who comes across this question. – DNS Apr 01 '10 at 13:27
  • The relevant link with code seems to return `502 – Bad Gateway` error, @jay. If possible, please fix it, since answers to many later questions point at this post. Thanks. – user3078414 Jul 07 '16 at 13:04