Getting the PID and Process Name From a dbus Caller in C

Over the past few months, I’ve been working on a dbus service (powerd) for Ubuntu Touch. Something that came up recently was the need to get the PID of the processes that call us. We were using this for statistics purposes of tracking who was holding requests, until today, when we decided to go a different direction. So this code is not landed in powerd, but perhaps it is still useful to someone. So I present, how to get the PID and process name from someone that calls you on dbus, in C.

This code assumes a few things. You need to have a working server that handles a call of some sort. We will plug into that call to get the PID of the caller. With that in mind, let’s get started. If you want the version of powerd that does this full async, it’s here: lp:~mfisch/+junk/powerd-pids. Note that this code also incorporates some statistics creation for powerd that is not going to be put into trunk in the form that it is in this branch. Anyway, onto the code:

Create a dbus proxy to make the PID look-up request to

We need a dbus proxy object to talk to. This is the service where we can lookup the PID given then dbus name of the connection. I will connect to this proxy asynchronously. In my “main”, I start the connection:

    /* proxy for getting PID info */
    g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
        G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
        NULL,
        "org.freedesktop.DBus",
        "/org/freedesktop/DBus",
        "org.freedesktop.DBus",
        NULL,
        (GAsyncReadyCallback)dbus_proxy_connect_cb,
        NULL);

And then finish it later, the main result here is that dbus_proxy is set so I can use it.:

void
dbus_proxy_connect_cb(GObject *source_object,
               GAsyncResult *res,
               gpointer user_data)
{
    GError *error = NULL;

    dbus_proxy = g_dbus_proxy_new_finish (res, &error);
    if (error) {
        g_warning("dbus_proxy_connect_cb failed: %s", error->message);
        g_error_free(error);
        dbus_proxy = NULL;
    }
    else {
        g_debug("dbus_proxy_connect_cb succeeded");
    }
}

In the call that your service handles, do the lookup synchronously

I have a synchronous lookup listed first, then an async one. You should use the async one because you’re a good coder… unless you need to block until you find out who is calling you for some reason. I’ve left some powerd-isms for the function call, the source is from the requestSysState method that powerd supports. We will use the dbus_proxy object we created above to request the PID.

gboolean                                                                       
handle_request_sys_state (PowerdSource *obj, GDBusMethodInvocation *invocation, int state)
{
    // get the name of the dbus object that called us
    owner = g_dbus_method_invocation_get_sender(invocation);
    if (dbus_proxy) {
        result = g_dbus_proxy_call_sync(dbus_proxy,
                "GetConnectionUnixProcessID",
                g_variant_new("(s)", owner),
                G_DBUS_CALL_FLAGS_NONE,
                -1,
                NULL,
                &error);
        if (error) {
            g_error("Unable to get PID for %s: %s", owner, error->message);
            g_error_free(error);
            error = NULL;
        }
        else {
            g_variant_get(result, "(u)", &owner_pid);
            g_info("request is from pid %d\n", owner_pid);
        }
    }
    ...
}

Once we have the PID, we can lookup the command line by reading /proc/PID/cmdline, my powerd code does this in the async example below.

async dbus for fun and profit

As I stated, synchronous is bad because it makes everyone wait, so here’s the async version.

gboolean                                                                       
handle_request_sys_state (PowerdSource *obj, GDBusMethodInvocation *invocation, int state)
{
    // get the name of the dbus object that called us
    owner = g_dbus_method_invocation_get_sender(invocation);
    g_dbus_proxy_call(dbus_proxy,
        "GetConnectionUnixProcessID",
        g_variant_new("(s)", dbus_name),
        G_DBUS_CALL_FLAGS_NONE,
        -1,
        NULL,
        (GAsyncReadyCallback)get_pid_from_dbus_name_cb,
        NULL);
    ...
}

Here’s our callback where we handle the results, I left the code in that reads the process name from /proc. We have a utility function called sysfs_read that I used.

void
get_pid_from_dbus_name_cb(GObject *source_object,
               GAsyncResult *res,
               gpointer user_data)
{
    GError *error = NULL;
    GVariant *result = NULL;
    guint pid;
    gchar process_name[PROCESS_NAME_LENGTH] = "";
    gchar proc_path[64] = "";
    int ret;

    result = g_dbus_proxy_call_finish (dbus_proxy, res, &error);
    if (error) {
        powerd_warn("get_pid_from_dbus_name_cb failed: %s", error->message);
        g_error_free(error);
    }
    else if (result) {
        g_variant_get(result, "(u)", &pid);
        g_variant_unref(result);
        /* safety check */
        if (pid != 0) {
            sprintf(proc_path, "/proc/%u/cmdline", pid);
            ret = sysfs_read(proc_path, process_name, PROCESS_NAME_LENGTH);
            if (ret < 0)
            {
                powerd_debug("error reading process name from %s: %d",
                    proc_path, ret);
                strcpy(process_name, "UNKNOWN");
            }
            g_debug("PID: %u, Process Name: %s", pid, process_name);
        }
        else {
            /* not sure this can happen */
            powerd_debug("unable to get pid info");
        }
    }
}

With that magic, I can get output like this:

PID: 4434, Process Name: ./powerd-cli
PID: 4436, Process Name: ./powerd-cli
...

But what about Python?

C is too hard you say. If you got carpal tunnel just from reading that code, I have a simple python call to do this for you, synchronously.

#!/usr/bin/python

import dbus
import sys

bus=dbus.SystemBus().get_object('org.freedesktop.DBus', '/org/freedesktop/DBus');
print dbus.Interface(bus, 'org.freedesktop.DBus').GetConnectionUnixProcessID(sys.argv[1]);

I use dbus-monitor to find someone interesting on dbus with this call. :1.17 looks like upower, so lets see if it worked:

mfisch@caprica:~$ ./foo.py :1.17
1988
mfisch@caprica:~$ cat /proc/1988/cmdline
/usr/lib/upower/upowerd

Looks right to me!

With the python, you can plug in the caller’s dbus name for “sys.argv[1]” and be on your way, or use the C code if you don’t want python’s overhead and think that managing pointers is entertaining.

Special thanks to Ted Gould who pointed me to this method.

Tagged ,

One thought on “Getting the PID and Process Name From a dbus Caller in C

  1. mdeslaur says:

    Pretty cool.

    Reminder: this shouldn’t be used as a security feature, as PIDs can roll-over and processes can modify their own argv.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>