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