client.c revision a1e1cf94
135c4bbdfSmrg/*
235c4bbdfSmrg * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). All
335c4bbdfSmrg * rights reserved.
435c4bbdfSmrg * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
535c4bbdfSmrg *
635c4bbdfSmrg * Permission is hereby granted, free of charge, to any person obtaining a copy
735c4bbdfSmrg * of this software and associated documentation files (the "Software"), to deal
835c4bbdfSmrg * in the Software without restriction, including without limitation the rights
935c4bbdfSmrg * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1035c4bbdfSmrg * copies of the Software, and to permit persons to whom the Software is
1135c4bbdfSmrg * furnished to do so, subject to the following conditions:
1235c4bbdfSmrg *
1335c4bbdfSmrg * The above copyright notice and this permission notice shall be included in
1435c4bbdfSmrg * all copies or substantial portions of the Software.
1535c4bbdfSmrg *
1635c4bbdfSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1735c4bbdfSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1835c4bbdfSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1935c4bbdfSmrg * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2035c4bbdfSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2135c4bbdfSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2235c4bbdfSmrg * THE SOFTWARE.
2335c4bbdfSmrg */
2435c4bbdfSmrg
2535c4bbdfSmrg/**
2635c4bbdfSmrg * @file
2735c4bbdfSmrg *
2835c4bbdfSmrg * This file contains functionality for identifying clients by various
2935c4bbdfSmrg * means. The primary purpose of identification is to simply aid in
3035c4bbdfSmrg * finding out which clients are using X server and how they are using
3135c4bbdfSmrg * it. For example, it's often necessary to monitor what requests
3235c4bbdfSmrg * clients are executing (to spot bad behaviour) and how they are
3335c4bbdfSmrg * allocating resources in X server (to spot excessive resource
3435c4bbdfSmrg * usage).
3535c4bbdfSmrg *
3635c4bbdfSmrg * This framework automatically allocates information, that can be
3735c4bbdfSmrg * used for client identification, when a client connects to the
3835c4bbdfSmrg * server. The information is freed when the client disconnects. The
3935c4bbdfSmrg * allocated information is just a collection of various IDs, such as
4035c4bbdfSmrg * PID and process name for local clients, that are likely to be
4135c4bbdfSmrg * useful in analyzing X server usage.
4235c4bbdfSmrg *
4335c4bbdfSmrg * Users of the framework can query ID information about clients at
4435c4bbdfSmrg * any time. To avoid repeated polling of IDs the users can also
4535c4bbdfSmrg * subscribe for notifications about the availability of ID
4635c4bbdfSmrg * information. IDs have been allocated before ClientStateCallback is
4735c4bbdfSmrg * called with ClientStateInitial state. Similarly the IDs will be
4835c4bbdfSmrg * released after ClientStateCallback is called with ClientStateGone
4935c4bbdfSmrg * state.
5035c4bbdfSmrg *
5135c4bbdfSmrg * Author: Rami Ylimäki <rami.ylimaki@vincit.fi>
5235c4bbdfSmrg */
5335c4bbdfSmrg
5435c4bbdfSmrg#include <sys/stat.h>
5535c4bbdfSmrg#include <fcntl.h>
5635c4bbdfSmrg#include <unistd.h>
5735c4bbdfSmrg
5835c4bbdfSmrg#include "client.h"
5935c4bbdfSmrg#include "os.h"
6035c4bbdfSmrg#include "dixstruct.h"
6135c4bbdfSmrg
6235c4bbdfSmrg#ifdef __sun
6335c4bbdfSmrg#include <errno.h>
6435c4bbdfSmrg#include <procfs.h>
6535c4bbdfSmrg#endif
6635c4bbdfSmrg
6735c4bbdfSmrg#ifdef __OpenBSD__
6835c4bbdfSmrg#include <sys/param.h>
6935c4bbdfSmrg#include <sys/sysctl.h>
7035c4bbdfSmrg#include <sys/types.h>
7135c4bbdfSmrg
7235c4bbdfSmrg#include <kvm.h>
7335c4bbdfSmrg#include <limits.h>
7435c4bbdfSmrg#endif
7535c4bbdfSmrg
76a1e1cf94Smrg#ifdef __APPLE__
77a1e1cf94Smrg#include <dispatch/dispatch.h>
78a1e1cf94Smrg#include <errno.h>
79a1e1cf94Smrg#include <sys/sysctl.h>
80a1e1cf94Smrg#endif
81a1e1cf94Smrg
8235c4bbdfSmrg/**
8335c4bbdfSmrg * Try to determine a PID for a client from its connection
8435c4bbdfSmrg * information. This should be called only once when new client has
8535c4bbdfSmrg * connected, use GetClientPid to determine the PID at other times.
8635c4bbdfSmrg *
8735c4bbdfSmrg * @param[in] client Connection linked to some process.
8835c4bbdfSmrg *
8935c4bbdfSmrg * @return PID of the client. Error (-1) if PID can't be determined
9035c4bbdfSmrg *         for the client.
9135c4bbdfSmrg *
9235c4bbdfSmrg * @see GetClientPid
9335c4bbdfSmrg */
9435c4bbdfSmrgpid_t
9535c4bbdfSmrgDetermineClientPid(struct _Client * client)
9635c4bbdfSmrg{
9735c4bbdfSmrg    LocalClientCredRec *lcc = NULL;
9835c4bbdfSmrg    pid_t pid = -1;
9935c4bbdfSmrg
10035c4bbdfSmrg    if (client == NullClient)
10135c4bbdfSmrg        return pid;
10235c4bbdfSmrg
10335c4bbdfSmrg    if (client == serverClient)
10435c4bbdfSmrg        return getpid();
10535c4bbdfSmrg
10635c4bbdfSmrg    if (GetLocalClientCreds(client, &lcc) != -1) {
10735c4bbdfSmrg        if (lcc->fieldsSet & LCC_PID_SET)
10835c4bbdfSmrg            pid = lcc->pid;
10935c4bbdfSmrg        FreeLocalClientCreds(lcc);
11035c4bbdfSmrg    }
11135c4bbdfSmrg
11235c4bbdfSmrg    return pid;
11335c4bbdfSmrg}
11435c4bbdfSmrg
11535c4bbdfSmrg/**
11635c4bbdfSmrg * Try to determine a command line string for a client based on its
11735c4bbdfSmrg * PID. Note that mapping PID to a command hasn't been implemented for
11835c4bbdfSmrg * some operating systems. This should be called only once when a new
11935c4bbdfSmrg * client has connected, use GetClientCmdName/Args to determine the
12035c4bbdfSmrg * string at other times.
12135c4bbdfSmrg *
12235c4bbdfSmrg * @param[in]  pid     Process ID of a client.
12335c4bbdfSmrg
12435c4bbdfSmrg * @param[out] cmdname Client process name without arguments. You must
12535c4bbdfSmrg *                     release this by calling free. On error NULL is
12635c4bbdfSmrg *                     returned. Pass NULL if you aren't interested in
12735c4bbdfSmrg *                     this value.
12835c4bbdfSmrg * @param[out] cmdargs Arguments to client process. Useful for
12935c4bbdfSmrg *                     identifying a client that is executed from a
13035c4bbdfSmrg *                     launcher program. You must release this by
13135c4bbdfSmrg *                     calling free. On error NULL is returned. Pass
13235c4bbdfSmrg *                     NULL if you aren't interested in this value.
13335c4bbdfSmrg *
13435c4bbdfSmrg * @see GetClientCmdName/Args
13535c4bbdfSmrg */
13635c4bbdfSmrgvoid
13735c4bbdfSmrgDetermineClientCmd(pid_t pid, const char **cmdname, const char **cmdargs)
13835c4bbdfSmrg{
139a1e1cf94Smrg#if !defined(__APPLE__)
14035c4bbdfSmrg    char path[PATH_MAX + 1];
14135c4bbdfSmrg    int totsize = 0;
14235c4bbdfSmrg    int fd = 0;
143a1e1cf94Smrg#endif
14435c4bbdfSmrg
14535c4bbdfSmrg    if (cmdname)
14635c4bbdfSmrg        *cmdname = NULL;
14735c4bbdfSmrg    if (cmdargs)
14835c4bbdfSmrg        *cmdargs = NULL;
14935c4bbdfSmrg
15035c4bbdfSmrg    if (pid == -1)
15135c4bbdfSmrg        return;
15235c4bbdfSmrg
153a1e1cf94Smrg#if defined (__APPLE__)
154a1e1cf94Smrg    {
155a1e1cf94Smrg        static dispatch_once_t once;
156a1e1cf94Smrg        static int argmax;
157a1e1cf94Smrg        dispatch_once(&once, ^{
158a1e1cf94Smrg            int mib[2];
159a1e1cf94Smrg            size_t len;
160a1e1cf94Smrg
161a1e1cf94Smrg            mib[0] = CTL_KERN;
162a1e1cf94Smrg            mib[1] = KERN_ARGMAX;
163a1e1cf94Smrg
164a1e1cf94Smrg            len = sizeof(argmax);
165a1e1cf94Smrg            if (sysctl(mib, 2, &argmax, &len, NULL, 0) == -1) {
166a1e1cf94Smrg                ErrorF("Unable to dynamically determine kern.argmax, using ARG_MAX (%d)\n", ARG_MAX);
167a1e1cf94Smrg                argmax = ARG_MAX;
168a1e1cf94Smrg            }
169a1e1cf94Smrg        });
170a1e1cf94Smrg
171a1e1cf94Smrg        int mib[3];
172a1e1cf94Smrg        size_t len = argmax;
173a1e1cf94Smrg        int32_t argc = -1;
174a1e1cf94Smrg
175a1e1cf94Smrg        char * const procargs = malloc(len);
176a1e1cf94Smrg        if (!procargs) {
177a1e1cf94Smrg            ErrorF("Failed to allocate memory (%lu bytes) for KERN_PROCARGS2 result for pid %d: %s\n", len, pid, strerror(errno));
178a1e1cf94Smrg            return;
179a1e1cf94Smrg        }
180a1e1cf94Smrg
181a1e1cf94Smrg        mib[0] = CTL_KERN;
182a1e1cf94Smrg        mib[1] = KERN_PROCARGS2;
183a1e1cf94Smrg        mib[2] = pid;
184a1e1cf94Smrg
185a1e1cf94Smrg        if (sysctl(mib, 3, procargs, &len, NULL, 0) == -1) {
186a1e1cf94Smrg            ErrorF("Failed to determine KERN_PROCARGS2 for pid %d: %s\n", pid, strerror(errno));
187a1e1cf94Smrg            free(procargs);
188a1e1cf94Smrg            return;
189a1e1cf94Smrg        }
190a1e1cf94Smrg
191a1e1cf94Smrg        if (len < sizeof(argc) || len > argmax) {
192a1e1cf94Smrg            ErrorF("Erroneous length returned when querying KERN_PROCARGS2 for pid %d: %zu\n", pid, len);
193a1e1cf94Smrg            free(procargs);
194a1e1cf94Smrg            return;
195a1e1cf94Smrg        }
196a1e1cf94Smrg
197a1e1cf94Smrg        /* Ensure we have a failsafe NUL termination just in case the last entry
198a1e1cf94Smrg         * was not actually NUL terminated.
199a1e1cf94Smrg         */
200a1e1cf94Smrg        procargs[len-1] = '\0';
201a1e1cf94Smrg
202a1e1cf94Smrg        /* Setup our iterator */
203a1e1cf94Smrg        char *is = procargs;
204a1e1cf94Smrg
205a1e1cf94Smrg        /* The first element in the buffer is argc as a 32bit int. When using
206a1e1cf94Smrg         * the older KERN_PROCARGS, this is omitted, and one needs to guess
207a1e1cf94Smrg         * (usually by checking for an `=` character) when we start seeing
208a1e1cf94Smrg         * envvars instead of arguments.
209a1e1cf94Smrg         */
210a1e1cf94Smrg        argc = *(int32_t *)is;
211a1e1cf94Smrg        is += sizeof(argc);
212a1e1cf94Smrg
213a1e1cf94Smrg        /* The very next string is the executable path.  Skip over it since
214a1e1cf94Smrg         * this function wants to return argv[0] and argv[1...n].
215a1e1cf94Smrg         */
216a1e1cf94Smrg        is += strlen(is) + 1;
217a1e1cf94Smrg
218a1e1cf94Smrg        /* Skip over extra NUL characters to get to the start of argv[0] */
219a1e1cf94Smrg        for (; (is < &procargs[len]) && !(*is); is++);
220a1e1cf94Smrg
221a1e1cf94Smrg        if (! (is < &procargs[len])) {
222a1e1cf94Smrg            ErrorF("Arguments were not returned when querying KERN_PROCARGS2 for pid %d: %zu\n", pid, len);
223a1e1cf94Smrg            free(procargs);
224a1e1cf94Smrg            return;
225a1e1cf94Smrg        }
226a1e1cf94Smrg
227a1e1cf94Smrg        if (cmdname) {
228a1e1cf94Smrg            *cmdname = strdup(is);
229a1e1cf94Smrg        }
230a1e1cf94Smrg
231a1e1cf94Smrg        /* Jump over argv[0] and point to argv[1] */
232a1e1cf94Smrg        is += strlen(is) + 1;
233a1e1cf94Smrg
234a1e1cf94Smrg        if (cmdargs && is < &procargs[len]) {
235a1e1cf94Smrg            char *args = is;
236a1e1cf94Smrg
237a1e1cf94Smrg            /* Remove the NUL terminators except the last one */
238a1e1cf94Smrg            for (int i = 1; i < argc - 1; i++) {
239a1e1cf94Smrg                /* Advance to the NUL terminator */
240a1e1cf94Smrg                is += strlen(is);
241a1e1cf94Smrg
242a1e1cf94Smrg                /* Change the NUL to a space, ensuring we don't accidentally remove the terminal NUL */
243a1e1cf94Smrg                if (is < &procargs[len-1]) {
244a1e1cf94Smrg                    *is = ' ';
245a1e1cf94Smrg                }
246a1e1cf94Smrg            }
247a1e1cf94Smrg
248a1e1cf94Smrg            *cmdargs = strdup(args);
249a1e1cf94Smrg        }
250a1e1cf94Smrg
251a1e1cf94Smrg        free(procargs);
252a1e1cf94Smrg    }
253a1e1cf94Smrg#elif defined(__OpenBSD__)
25435c4bbdfSmrg    /* on OpenBSD use kvm_getargv() */
25535c4bbdfSmrg    {
25635c4bbdfSmrg        kvm_t *kd;
25735c4bbdfSmrg        char errbuf[_POSIX2_LINE_MAX];
25835c4bbdfSmrg        char **argv;
25935c4bbdfSmrg        struct kinfo_proc *kp;
26035c4bbdfSmrg        size_t len = 0;
26135c4bbdfSmrg        int i, n;
26235c4bbdfSmrg
26335c4bbdfSmrg        kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
26435c4bbdfSmrg        if (kd == NULL)
26535c4bbdfSmrg            return;
26635c4bbdfSmrg        kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(struct kinfo_proc),
26735c4bbdfSmrg                          &n);
26835c4bbdfSmrg        if (n != 1)
26935c4bbdfSmrg            return;
27035c4bbdfSmrg        argv = kvm_getargv(kd, kp, 0);
27135c4bbdfSmrg        *cmdname = strdup(argv[0]);
27235c4bbdfSmrg        i = 1;
27335c4bbdfSmrg        while (argv[i] != NULL) {
27435c4bbdfSmrg            len += strlen(argv[i]) + 1;
27535c4bbdfSmrg            i++;
27635c4bbdfSmrg        }
27735c4bbdfSmrg        *cmdargs = calloc(1, len);
27835c4bbdfSmrg        i = 1;
27935c4bbdfSmrg        while (argv[i] != NULL) {
28035c4bbdfSmrg            strlcat(*cmdargs, argv[i], len);
28135c4bbdfSmrg            strlcat(*cmdargs, " ", len);
28235c4bbdfSmrg            i++;
28335c4bbdfSmrg        }
28435c4bbdfSmrg        kvm_close(kd);
28535c4bbdfSmrg    }
28635c4bbdfSmrg#else                           /* Linux using /proc/pid/cmdline */
28735c4bbdfSmrg
28835c4bbdfSmrg    /* Check if /proc/pid/cmdline exists. It's not supported on all
28935c4bbdfSmrg     * operating systems. */
29035c4bbdfSmrg    if (snprintf(path, sizeof(path), "/proc/%d/cmdline", pid) < 0)
29135c4bbdfSmrg        return;
29235c4bbdfSmrg    fd = open(path, O_RDONLY);
29335c4bbdfSmrg    if (fd < 0)
2941b5d61b8Smrg#ifdef __sun
2951b5d61b8Smrg        goto fallback;
2961b5d61b8Smrg#else
29735c4bbdfSmrg        return;
2981b5d61b8Smrg#endif
29935c4bbdfSmrg
30035c4bbdfSmrg    /* Read the contents of /proc/pid/cmdline. It should contain the
30135c4bbdfSmrg     * process name and arguments. */
30235c4bbdfSmrg    totsize = read(fd, path, sizeof(path));
30335c4bbdfSmrg    close(fd);
30435c4bbdfSmrg    if (totsize <= 0)
30535c4bbdfSmrg        return;
30635c4bbdfSmrg    path[totsize - 1] = '\0';
30735c4bbdfSmrg
308ed6184dfSmrg    /* Construct the process name without arguments. */
30935c4bbdfSmrg    if (cmdname) {
31035c4bbdfSmrg        *cmdname = strdup(path);
31135c4bbdfSmrg    }
31235c4bbdfSmrg
31335c4bbdfSmrg    /* Construct the arguments for client process. */
31435c4bbdfSmrg    if (cmdargs) {
31535c4bbdfSmrg        int cmdsize = strlen(path) + 1;
31635c4bbdfSmrg        int argsize = totsize - cmdsize;
31735c4bbdfSmrg        char *args = NULL;
31835c4bbdfSmrg
31935c4bbdfSmrg        if (argsize > 0)
32035c4bbdfSmrg            args = malloc(argsize);
32135c4bbdfSmrg        if (args) {
32235c4bbdfSmrg            int i = 0;
32335c4bbdfSmrg
32435c4bbdfSmrg            for (i = 0; i < (argsize - 1); ++i) {
32535c4bbdfSmrg                const char c = path[cmdsize + i];
32635c4bbdfSmrg
32735c4bbdfSmrg                args[i] = (c == '\0') ? ' ' : c;
32835c4bbdfSmrg            }
32935c4bbdfSmrg            args[argsize - 1] = '\0';
33035c4bbdfSmrg            *cmdargs = args;
33135c4bbdfSmrg        }
33235c4bbdfSmrg    }
3331b5d61b8Smrg    return;
3341b5d61b8Smrg#endif
3351b5d61b8Smrg
3361b5d61b8Smrg#ifdef __sun                    /* Solaris */
3371b5d61b8Smrg  fallback:
3381b5d61b8Smrg    /* Solaris prior to 11.3.5 does not support /proc/pid/cmdline, but
3391b5d61b8Smrg     * makes information similar to what ps shows available in a binary
3401b5d61b8Smrg     * structure in the /proc/pid/psinfo file. */
3411b5d61b8Smrg    if (snprintf(path, sizeof(path), "/proc/%d/psinfo", pid) < 0)
3421b5d61b8Smrg        return;
3431b5d61b8Smrg    fd = open(path, O_RDONLY);
3441b5d61b8Smrg    if (fd < 0) {
3451b5d61b8Smrg        ErrorF("Failed to open %s: %s\n", path, strerror(errno));
3461b5d61b8Smrg        return;
3471b5d61b8Smrg    }
3481b5d61b8Smrg    else {
3491b5d61b8Smrg        psinfo_t psinfo = { 0 };
3501b5d61b8Smrg        char *sp;
3511b5d61b8Smrg
3521b5d61b8Smrg        totsize = read(fd, &psinfo, sizeof(psinfo_t));
3531b5d61b8Smrg        close(fd);
3541b5d61b8Smrg        if (totsize <= 0)
3551b5d61b8Smrg            return;
3561b5d61b8Smrg
3571b5d61b8Smrg        /* pr_psargs is the first PRARGSZ (80) characters of the command
3581b5d61b8Smrg         * line string - assume up to the first space is the command name,
3591b5d61b8Smrg         * since it's not delimited.   While there is also pr_fname, that's
3601b5d61b8Smrg         * more limited, giving only the first 16 chars of the basename of
3611b5d61b8Smrg         * the file that was exec'ed, thus cutting off many long gnome
3621b5d61b8Smrg         * command names, or returning "isapython2.6" for all python scripts.
3631b5d61b8Smrg         */
3641b5d61b8Smrg        psinfo.pr_psargs[PRARGSZ - 1] = '\0';
3651b5d61b8Smrg        sp = strchr(psinfo.pr_psargs, ' ');
3661b5d61b8Smrg        if (sp)
3671b5d61b8Smrg            *sp++ = '\0';
3681b5d61b8Smrg
3691b5d61b8Smrg        if (cmdname)
3701b5d61b8Smrg            *cmdname = strdup(psinfo.pr_psargs);
3711b5d61b8Smrg
3721b5d61b8Smrg        if (cmdargs && sp)
3731b5d61b8Smrg            *cmdargs = strdup(sp);
3741b5d61b8Smrg    }
37535c4bbdfSmrg#endif
37635c4bbdfSmrg}
37735c4bbdfSmrg
37835c4bbdfSmrg/**
37935c4bbdfSmrg * Called when a new client connects. Allocates client ID information.
38035c4bbdfSmrg *
38135c4bbdfSmrg * @param[in] client Recently connected client.
38235c4bbdfSmrg */
38335c4bbdfSmrgvoid
38435c4bbdfSmrgReserveClientIds(struct _Client *client)
38535c4bbdfSmrg{
38635c4bbdfSmrg#ifdef CLIENTIDS
38735c4bbdfSmrg    if (client == NullClient)
38835c4bbdfSmrg        return;
38935c4bbdfSmrg
39035c4bbdfSmrg    assert(!client->clientIds);
39135c4bbdfSmrg    client->clientIds = calloc(1, sizeof(ClientIdRec));
39235c4bbdfSmrg    if (!client->clientIds)
39335c4bbdfSmrg        return;
39435c4bbdfSmrg
39535c4bbdfSmrg    client->clientIds->pid = DetermineClientPid(client);
39635c4bbdfSmrg    if (client->clientIds->pid != -1)
39735c4bbdfSmrg        DetermineClientCmd(client->clientIds->pid, &client->clientIds->cmdname,
39835c4bbdfSmrg                           &client->clientIds->cmdargs);
39935c4bbdfSmrg
40035c4bbdfSmrg    DebugF("client(%lx): Reserved pid(%d).\n",
40135c4bbdfSmrg           (unsigned long) client->clientAsMask, client->clientIds->pid);
40235c4bbdfSmrg    DebugF("client(%lx): Reserved cmdname(%s) and cmdargs(%s).\n",
40335c4bbdfSmrg           (unsigned long) client->clientAsMask,
40435c4bbdfSmrg           client->clientIds->cmdname ? client->clientIds->cmdname : "NULL",
40535c4bbdfSmrg           client->clientIds->cmdargs ? client->clientIds->cmdargs : "NULL");
40635c4bbdfSmrg#endif                          /* CLIENTIDS */
40735c4bbdfSmrg}
40835c4bbdfSmrg
40935c4bbdfSmrg/**
41035c4bbdfSmrg * Called when an existing client disconnects. Frees client ID
41135c4bbdfSmrg * information.
41235c4bbdfSmrg *
41335c4bbdfSmrg * @param[in] client Recently disconnected client.
41435c4bbdfSmrg */
41535c4bbdfSmrgvoid
41635c4bbdfSmrgReleaseClientIds(struct _Client *client)
41735c4bbdfSmrg{
41835c4bbdfSmrg#ifdef CLIENTIDS
41935c4bbdfSmrg    if (client == NullClient)
42035c4bbdfSmrg        return;
42135c4bbdfSmrg
42235c4bbdfSmrg    if (!client->clientIds)
42335c4bbdfSmrg        return;
42435c4bbdfSmrg
42535c4bbdfSmrg    DebugF("client(%lx): Released pid(%d).\n",
42635c4bbdfSmrg           (unsigned long) client->clientAsMask, client->clientIds->pid);
42735c4bbdfSmrg    DebugF("client(%lx): Released cmdline(%s) and cmdargs(%s).\n",
42835c4bbdfSmrg           (unsigned long) client->clientAsMask,
42935c4bbdfSmrg           client->clientIds->cmdname ? client->clientIds->cmdname : "NULL",
43035c4bbdfSmrg           client->clientIds->cmdargs ? client->clientIds->cmdargs : "NULL");
43135c4bbdfSmrg
43235c4bbdfSmrg    free((void *) client->clientIds->cmdname);  /* const char * */
43335c4bbdfSmrg    free((void *) client->clientIds->cmdargs);  /* const char * */
43435c4bbdfSmrg    free(client->clientIds);
43535c4bbdfSmrg    client->clientIds = NULL;
43635c4bbdfSmrg#endif                          /* CLIENTIDS */
43735c4bbdfSmrg}
43835c4bbdfSmrg
43935c4bbdfSmrg/**
44035c4bbdfSmrg * Get cached PID of a client.
44135c4bbdfSmrg *
44235c4bbdfSmrg * param[in] client Client whose PID has been already cached.
44335c4bbdfSmrg *
44435c4bbdfSmrg * @return Cached client PID. Error (-1) if called:
44535c4bbdfSmrg *         - before ClientStateInitial client state notification
44635c4bbdfSmrg *         - after ClientStateGone client state notification
44735c4bbdfSmrg *         - for remote clients
44835c4bbdfSmrg *
44935c4bbdfSmrg * @see DetermineClientPid
45035c4bbdfSmrg */
45135c4bbdfSmrgpid_t
45235c4bbdfSmrgGetClientPid(struct _Client *client)
45335c4bbdfSmrg{
45435c4bbdfSmrg    if (client == NullClient)
45535c4bbdfSmrg        return -1;
45635c4bbdfSmrg
45735c4bbdfSmrg    if (!client->clientIds)
45835c4bbdfSmrg        return -1;
45935c4bbdfSmrg
46035c4bbdfSmrg    return client->clientIds->pid;
46135c4bbdfSmrg}
46235c4bbdfSmrg
46335c4bbdfSmrg/**
46435c4bbdfSmrg * Get cached command name string of a client.
46535c4bbdfSmrg *
46635c4bbdfSmrg * param[in] client Client whose command line string has been already
46735c4bbdfSmrg *                  cached.
46835c4bbdfSmrg *
46935c4bbdfSmrg * @return Cached client command name. Error (NULL) if called:
47035c4bbdfSmrg *         - before ClientStateInitial client state notification
47135c4bbdfSmrg *         - after ClientStateGone client state notification
47235c4bbdfSmrg *         - for remote clients
47335c4bbdfSmrg *         - on OS that doesn't support mapping of PID to command line
47435c4bbdfSmrg *
47535c4bbdfSmrg * @see DetermineClientCmd
47635c4bbdfSmrg */
47735c4bbdfSmrgconst char *
47835c4bbdfSmrgGetClientCmdName(struct _Client *client)
47935c4bbdfSmrg{
48035c4bbdfSmrg    if (client == NullClient)
48135c4bbdfSmrg        return NULL;
48235c4bbdfSmrg
48335c4bbdfSmrg    if (!client->clientIds)
48435c4bbdfSmrg        return NULL;
48535c4bbdfSmrg
48635c4bbdfSmrg    return client->clientIds->cmdname;
48735c4bbdfSmrg}
48835c4bbdfSmrg
48935c4bbdfSmrg/**
49035c4bbdfSmrg * Get cached command arguments string of a client.
49135c4bbdfSmrg *
49235c4bbdfSmrg * param[in] client Client whose command line string has been already
49335c4bbdfSmrg *                  cached.
49435c4bbdfSmrg *
49535c4bbdfSmrg * @return Cached client command arguments. Error (NULL) if called:
49635c4bbdfSmrg *         - before ClientStateInitial client state notification
49735c4bbdfSmrg *         - after ClientStateGone client state notification
49835c4bbdfSmrg *         - for remote clients
49935c4bbdfSmrg *         - on OS that doesn't support mapping of PID to command line
50035c4bbdfSmrg *
50135c4bbdfSmrg * @see DetermineClientCmd
50235c4bbdfSmrg */
50335c4bbdfSmrgconst char *
50435c4bbdfSmrgGetClientCmdArgs(struct _Client *client)
50535c4bbdfSmrg{
50635c4bbdfSmrg    if (client == NullClient)
50735c4bbdfSmrg        return NULL;
50835c4bbdfSmrg
50935c4bbdfSmrg    if (!client->clientIds)
51035c4bbdfSmrg        return NULL;
51135c4bbdfSmrg
51235c4bbdfSmrg    return client->clientIds->cmdargs;
51335c4bbdfSmrg}
514