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);
27158cf2af7Smrg        if (cmdname) {
27258cf2af7Smrg            if (argv == NULL || argv[0] == NULL)
27358cf2af7Smrg                return;
27458cf2af7Smrg            else
27558cf2af7Smrg                *cmdname = strdup(argv[0]);
27635c4bbdfSmrg        }
27758cf2af7Smrg        if (cmdargs) {
27858cf2af7Smrg            i = 1;
27958cf2af7Smrg            while (argv[i] != NULL) {
28058cf2af7Smrg                len += strlen(argv[i]) + 1;
28158cf2af7Smrg                i++;
28258cf2af7Smrg            }
28358cf2af7Smrg            *cmdargs = calloc(1, len);
28458cf2af7Smrg            i = 1;
28558cf2af7Smrg            while (argv[i] != NULL) {
28658cf2af7Smrg                strlcat(*(char **)cmdargs, argv[i], len);
28758cf2af7Smrg                strlcat(*(char **)cmdargs, " ", len);
28858cf2af7Smrg                i++;
28958cf2af7Smrg            }
29035c4bbdfSmrg        }
29135c4bbdfSmrg        kvm_close(kd);
29235c4bbdfSmrg    }
29335c4bbdfSmrg#else                           /* Linux using /proc/pid/cmdline */
29435c4bbdfSmrg
29535c4bbdfSmrg    /* Check if /proc/pid/cmdline exists. It's not supported on all
29635c4bbdfSmrg     * operating systems. */
29735c4bbdfSmrg    if (snprintf(path, sizeof(path), "/proc/%d/cmdline", pid) < 0)
29835c4bbdfSmrg        return;
29935c4bbdfSmrg    fd = open(path, O_RDONLY);
30035c4bbdfSmrg    if (fd < 0)
3011b5d61b8Smrg#ifdef __sun
3021b5d61b8Smrg        goto fallback;
3031b5d61b8Smrg#else
30435c4bbdfSmrg        return;
3051b5d61b8Smrg#endif
30635c4bbdfSmrg
30735c4bbdfSmrg    /* Read the contents of /proc/pid/cmdline. It should contain the
30835c4bbdfSmrg     * process name and arguments. */
30935c4bbdfSmrg    totsize = read(fd, path, sizeof(path));
31035c4bbdfSmrg    close(fd);
31135c4bbdfSmrg    if (totsize <= 0)
31235c4bbdfSmrg        return;
31335c4bbdfSmrg    path[totsize - 1] = '\0';
31435c4bbdfSmrg
315ed6184dfSmrg    /* Construct the process name without arguments. */
31635c4bbdfSmrg    if (cmdname) {
31735c4bbdfSmrg        *cmdname = strdup(path);
31835c4bbdfSmrg    }
31935c4bbdfSmrg
32035c4bbdfSmrg    /* Construct the arguments for client process. */
32135c4bbdfSmrg    if (cmdargs) {
32235c4bbdfSmrg        int cmdsize = strlen(path) + 1;
32335c4bbdfSmrg        int argsize = totsize - cmdsize;
32435c4bbdfSmrg        char *args = NULL;
32535c4bbdfSmrg
32635c4bbdfSmrg        if (argsize > 0)
32735c4bbdfSmrg            args = malloc(argsize);
32835c4bbdfSmrg        if (args) {
32935c4bbdfSmrg            int i = 0;
33035c4bbdfSmrg
33135c4bbdfSmrg            for (i = 0; i < (argsize - 1); ++i) {
33235c4bbdfSmrg                const char c = path[cmdsize + i];
33335c4bbdfSmrg
33435c4bbdfSmrg                args[i] = (c == '\0') ? ' ' : c;
33535c4bbdfSmrg            }
33635c4bbdfSmrg            args[argsize - 1] = '\0';
33735c4bbdfSmrg            *cmdargs = args;
33835c4bbdfSmrg        }
33935c4bbdfSmrg    }
3401b5d61b8Smrg    return;
3411b5d61b8Smrg#endif
3421b5d61b8Smrg
3431b5d61b8Smrg#ifdef __sun                    /* Solaris */
3441b5d61b8Smrg  fallback:
3451b5d61b8Smrg    /* Solaris prior to 11.3.5 does not support /proc/pid/cmdline, but
3461b5d61b8Smrg     * makes information similar to what ps shows available in a binary
3471b5d61b8Smrg     * structure in the /proc/pid/psinfo file. */
3481b5d61b8Smrg    if (snprintf(path, sizeof(path), "/proc/%d/psinfo", pid) < 0)
3491b5d61b8Smrg        return;
3501b5d61b8Smrg    fd = open(path, O_RDONLY);
3511b5d61b8Smrg    if (fd < 0) {
3521b5d61b8Smrg        ErrorF("Failed to open %s: %s\n", path, strerror(errno));
3531b5d61b8Smrg        return;
3541b5d61b8Smrg    }
3551b5d61b8Smrg    else {
3561b5d61b8Smrg        psinfo_t psinfo = { 0 };
3571b5d61b8Smrg        char *sp;
3581b5d61b8Smrg
3591b5d61b8Smrg        totsize = read(fd, &psinfo, sizeof(psinfo_t));
3601b5d61b8Smrg        close(fd);
3611b5d61b8Smrg        if (totsize <= 0)
3621b5d61b8Smrg            return;
3631b5d61b8Smrg
3641b5d61b8Smrg        /* pr_psargs is the first PRARGSZ (80) characters of the command
3651b5d61b8Smrg         * line string - assume up to the first space is the command name,
3661b5d61b8Smrg         * since it's not delimited.   While there is also pr_fname, that's
3671b5d61b8Smrg         * more limited, giving only the first 16 chars of the basename of
3681b5d61b8Smrg         * the file that was exec'ed, thus cutting off many long gnome
3691b5d61b8Smrg         * command names, or returning "isapython2.6" for all python scripts.
3701b5d61b8Smrg         */
3711b5d61b8Smrg        psinfo.pr_psargs[PRARGSZ - 1] = '\0';
3721b5d61b8Smrg        sp = strchr(psinfo.pr_psargs, ' ');
3731b5d61b8Smrg        if (sp)
3741b5d61b8Smrg            *sp++ = '\0';
3751b5d61b8Smrg
3761b5d61b8Smrg        if (cmdname)
3771b5d61b8Smrg            *cmdname = strdup(psinfo.pr_psargs);
3781b5d61b8Smrg
3791b5d61b8Smrg        if (cmdargs && sp)
3801b5d61b8Smrg            *cmdargs = strdup(sp);
3811b5d61b8Smrg    }
38235c4bbdfSmrg#endif
38335c4bbdfSmrg}
38435c4bbdfSmrg
38535c4bbdfSmrg/**
38635c4bbdfSmrg * Called when a new client connects. Allocates client ID information.
38735c4bbdfSmrg *
38835c4bbdfSmrg * @param[in] client Recently connected client.
38935c4bbdfSmrg */
39035c4bbdfSmrgvoid
39135c4bbdfSmrgReserveClientIds(struct _Client *client)
39235c4bbdfSmrg{
39335c4bbdfSmrg#ifdef CLIENTIDS
39435c4bbdfSmrg    if (client == NullClient)
39535c4bbdfSmrg        return;
39635c4bbdfSmrg
39735c4bbdfSmrg    assert(!client->clientIds);
39835c4bbdfSmrg    client->clientIds = calloc(1, sizeof(ClientIdRec));
39935c4bbdfSmrg    if (!client->clientIds)
40035c4bbdfSmrg        return;
40135c4bbdfSmrg
40235c4bbdfSmrg    client->clientIds->pid = DetermineClientPid(client);
40335c4bbdfSmrg    if (client->clientIds->pid != -1)
40435c4bbdfSmrg        DetermineClientCmd(client->clientIds->pid, &client->clientIds->cmdname,
40535c4bbdfSmrg                           &client->clientIds->cmdargs);
40635c4bbdfSmrg
40735c4bbdfSmrg    DebugF("client(%lx): Reserved pid(%d).\n",
40835c4bbdfSmrg           (unsigned long) client->clientAsMask, client->clientIds->pid);
40935c4bbdfSmrg    DebugF("client(%lx): Reserved cmdname(%s) and cmdargs(%s).\n",
41035c4bbdfSmrg           (unsigned long) client->clientAsMask,
41135c4bbdfSmrg           client->clientIds->cmdname ? client->clientIds->cmdname : "NULL",
41235c4bbdfSmrg           client->clientIds->cmdargs ? client->clientIds->cmdargs : "NULL");
41335c4bbdfSmrg#endif                          /* CLIENTIDS */
41435c4bbdfSmrg}
41535c4bbdfSmrg
41635c4bbdfSmrg/**
41735c4bbdfSmrg * Called when an existing client disconnects. Frees client ID
41835c4bbdfSmrg * information.
41935c4bbdfSmrg *
42035c4bbdfSmrg * @param[in] client Recently disconnected client.
42135c4bbdfSmrg */
42235c4bbdfSmrgvoid
42335c4bbdfSmrgReleaseClientIds(struct _Client *client)
42435c4bbdfSmrg{
42535c4bbdfSmrg#ifdef CLIENTIDS
42635c4bbdfSmrg    if (client == NullClient)
42735c4bbdfSmrg        return;
42835c4bbdfSmrg
42935c4bbdfSmrg    if (!client->clientIds)
43035c4bbdfSmrg        return;
43135c4bbdfSmrg
43235c4bbdfSmrg    DebugF("client(%lx): Released pid(%d).\n",
43335c4bbdfSmrg           (unsigned long) client->clientAsMask, client->clientIds->pid);
43435c4bbdfSmrg    DebugF("client(%lx): Released cmdline(%s) and cmdargs(%s).\n",
43535c4bbdfSmrg           (unsigned long) client->clientAsMask,
43635c4bbdfSmrg           client->clientIds->cmdname ? client->clientIds->cmdname : "NULL",
43735c4bbdfSmrg           client->clientIds->cmdargs ? client->clientIds->cmdargs : "NULL");
43835c4bbdfSmrg
43935c4bbdfSmrg    free((void *) client->clientIds->cmdname);  /* const char * */
44035c4bbdfSmrg    free((void *) client->clientIds->cmdargs);  /* const char * */
44135c4bbdfSmrg    free(client->clientIds);
44235c4bbdfSmrg    client->clientIds = NULL;
44335c4bbdfSmrg#endif                          /* CLIENTIDS */
44435c4bbdfSmrg}
44535c4bbdfSmrg
44635c4bbdfSmrg/**
44735c4bbdfSmrg * Get cached PID of a client.
44835c4bbdfSmrg *
44935c4bbdfSmrg * param[in] client Client whose PID has been already cached.
45035c4bbdfSmrg *
45135c4bbdfSmrg * @return Cached client PID. Error (-1) if called:
45235c4bbdfSmrg *         - before ClientStateInitial client state notification
45335c4bbdfSmrg *         - after ClientStateGone client state notification
45435c4bbdfSmrg *         - for remote clients
45535c4bbdfSmrg *
45635c4bbdfSmrg * @see DetermineClientPid
45735c4bbdfSmrg */
45835c4bbdfSmrgpid_t
45935c4bbdfSmrgGetClientPid(struct _Client *client)
46035c4bbdfSmrg{
46135c4bbdfSmrg    if (client == NullClient)
46235c4bbdfSmrg        return -1;
46335c4bbdfSmrg
46435c4bbdfSmrg    if (!client->clientIds)
46535c4bbdfSmrg        return -1;
46635c4bbdfSmrg
46735c4bbdfSmrg    return client->clientIds->pid;
46835c4bbdfSmrg}
46935c4bbdfSmrg
47035c4bbdfSmrg/**
47135c4bbdfSmrg * Get cached command name string of a client.
47235c4bbdfSmrg *
47335c4bbdfSmrg * param[in] client Client whose command line string has been already
47435c4bbdfSmrg *                  cached.
47535c4bbdfSmrg *
47635c4bbdfSmrg * @return Cached client command name. Error (NULL) if called:
47735c4bbdfSmrg *         - before ClientStateInitial client state notification
47835c4bbdfSmrg *         - after ClientStateGone client state notification
47935c4bbdfSmrg *         - for remote clients
48035c4bbdfSmrg *         - on OS that doesn't support mapping of PID to command line
48135c4bbdfSmrg *
48235c4bbdfSmrg * @see DetermineClientCmd
48335c4bbdfSmrg */
48435c4bbdfSmrgconst char *
48535c4bbdfSmrgGetClientCmdName(struct _Client *client)
48635c4bbdfSmrg{
48735c4bbdfSmrg    if (client == NullClient)
48835c4bbdfSmrg        return NULL;
48935c4bbdfSmrg
49035c4bbdfSmrg    if (!client->clientIds)
49135c4bbdfSmrg        return NULL;
49235c4bbdfSmrg
49335c4bbdfSmrg    return client->clientIds->cmdname;
49435c4bbdfSmrg}
49535c4bbdfSmrg
49635c4bbdfSmrg/**
49735c4bbdfSmrg * Get cached command arguments string of a client.
49835c4bbdfSmrg *
49935c4bbdfSmrg * param[in] client Client whose command line string has been already
50035c4bbdfSmrg *                  cached.
50135c4bbdfSmrg *
50235c4bbdfSmrg * @return Cached client command arguments. Error (NULL) if called:
50335c4bbdfSmrg *         - before ClientStateInitial client state notification
50435c4bbdfSmrg *         - after ClientStateGone client state notification
50535c4bbdfSmrg *         - for remote clients
50635c4bbdfSmrg *         - on OS that doesn't support mapping of PID to command line
50735c4bbdfSmrg *
50835c4bbdfSmrg * @see DetermineClientCmd
50935c4bbdfSmrg */
51035c4bbdfSmrgconst char *
51135c4bbdfSmrgGetClientCmdArgs(struct _Client *client)
51235c4bbdfSmrg{
51335c4bbdfSmrg    if (client == NullClient)
51435c4bbdfSmrg        return NULL;
51535c4bbdfSmrg
51635c4bbdfSmrg    if (!client->clientIds)
51735c4bbdfSmrg        return NULL;
51835c4bbdfSmrg
51935c4bbdfSmrg    return client->clientIds->cmdargs;
52035c4bbdfSmrg}
521