client.c revision 1b5d61b8
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 7635c4bbdfSmrg/** 7735c4bbdfSmrg * Try to determine a PID for a client from its connection 7835c4bbdfSmrg * information. This should be called only once when new client has 7935c4bbdfSmrg * connected, use GetClientPid to determine the PID at other times. 8035c4bbdfSmrg * 8135c4bbdfSmrg * @param[in] client Connection linked to some process. 8235c4bbdfSmrg * 8335c4bbdfSmrg * @return PID of the client. Error (-1) if PID can't be determined 8435c4bbdfSmrg * for the client. 8535c4bbdfSmrg * 8635c4bbdfSmrg * @see GetClientPid 8735c4bbdfSmrg */ 8835c4bbdfSmrgpid_t 8935c4bbdfSmrgDetermineClientPid(struct _Client * client) 9035c4bbdfSmrg{ 9135c4bbdfSmrg LocalClientCredRec *lcc = NULL; 9235c4bbdfSmrg pid_t pid = -1; 9335c4bbdfSmrg 9435c4bbdfSmrg if (client == NullClient) 9535c4bbdfSmrg return pid; 9635c4bbdfSmrg 9735c4bbdfSmrg if (client == serverClient) 9835c4bbdfSmrg return getpid(); 9935c4bbdfSmrg 10035c4bbdfSmrg if (GetLocalClientCreds(client, &lcc) != -1) { 10135c4bbdfSmrg if (lcc->fieldsSet & LCC_PID_SET) 10235c4bbdfSmrg pid = lcc->pid; 10335c4bbdfSmrg FreeLocalClientCreds(lcc); 10435c4bbdfSmrg } 10535c4bbdfSmrg 10635c4bbdfSmrg return pid; 10735c4bbdfSmrg} 10835c4bbdfSmrg 10935c4bbdfSmrg/** 11035c4bbdfSmrg * Try to determine a command line string for a client based on its 11135c4bbdfSmrg * PID. Note that mapping PID to a command hasn't been implemented for 11235c4bbdfSmrg * some operating systems. This should be called only once when a new 11335c4bbdfSmrg * client has connected, use GetClientCmdName/Args to determine the 11435c4bbdfSmrg * string at other times. 11535c4bbdfSmrg * 11635c4bbdfSmrg * @param[in] pid Process ID of a client. 11735c4bbdfSmrg 11835c4bbdfSmrg * @param[out] cmdname Client process name without arguments. You must 11935c4bbdfSmrg * release this by calling free. On error NULL is 12035c4bbdfSmrg * returned. Pass NULL if you aren't interested in 12135c4bbdfSmrg * this value. 12235c4bbdfSmrg * @param[out] cmdargs Arguments to client process. Useful for 12335c4bbdfSmrg * identifying a client that is executed from a 12435c4bbdfSmrg * launcher program. You must release this by 12535c4bbdfSmrg * calling free. On error NULL is returned. Pass 12635c4bbdfSmrg * NULL if you aren't interested in this value. 12735c4bbdfSmrg * 12835c4bbdfSmrg * @see GetClientCmdName/Args 12935c4bbdfSmrg */ 13035c4bbdfSmrgvoid 13135c4bbdfSmrgDetermineClientCmd(pid_t pid, const char **cmdname, const char **cmdargs) 13235c4bbdfSmrg{ 13335c4bbdfSmrg char path[PATH_MAX + 1]; 13435c4bbdfSmrg int totsize = 0; 13535c4bbdfSmrg int fd = 0; 13635c4bbdfSmrg 13735c4bbdfSmrg if (cmdname) 13835c4bbdfSmrg *cmdname = NULL; 13935c4bbdfSmrg if (cmdargs) 14035c4bbdfSmrg *cmdargs = NULL; 14135c4bbdfSmrg 14235c4bbdfSmrg if (pid == -1) 14335c4bbdfSmrg return; 14435c4bbdfSmrg 1451b5d61b8Smrg#if defined(__OpenBSD__) 14635c4bbdfSmrg /* on OpenBSD use kvm_getargv() */ 14735c4bbdfSmrg { 14835c4bbdfSmrg kvm_t *kd; 14935c4bbdfSmrg char errbuf[_POSIX2_LINE_MAX]; 15035c4bbdfSmrg char **argv; 15135c4bbdfSmrg struct kinfo_proc *kp; 15235c4bbdfSmrg size_t len = 0; 15335c4bbdfSmrg int i, n; 15435c4bbdfSmrg 15535c4bbdfSmrg kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 15635c4bbdfSmrg if (kd == NULL) 15735c4bbdfSmrg return; 15835c4bbdfSmrg kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(struct kinfo_proc), 15935c4bbdfSmrg &n); 16035c4bbdfSmrg if (n != 1) 16135c4bbdfSmrg return; 16235c4bbdfSmrg argv = kvm_getargv(kd, kp, 0); 16335c4bbdfSmrg *cmdname = strdup(argv[0]); 16435c4bbdfSmrg i = 1; 16535c4bbdfSmrg while (argv[i] != NULL) { 16635c4bbdfSmrg len += strlen(argv[i]) + 1; 16735c4bbdfSmrg i++; 16835c4bbdfSmrg } 16935c4bbdfSmrg *cmdargs = calloc(1, len); 17035c4bbdfSmrg i = 1; 17135c4bbdfSmrg while (argv[i] != NULL) { 17235c4bbdfSmrg strlcat(*cmdargs, argv[i], len); 17335c4bbdfSmrg strlcat(*cmdargs, " ", len); 17435c4bbdfSmrg i++; 17535c4bbdfSmrg } 17635c4bbdfSmrg kvm_close(kd); 17735c4bbdfSmrg } 17835c4bbdfSmrg#else /* Linux using /proc/pid/cmdline */ 17935c4bbdfSmrg 18035c4bbdfSmrg /* Check if /proc/pid/cmdline exists. It's not supported on all 18135c4bbdfSmrg * operating systems. */ 18235c4bbdfSmrg if (snprintf(path, sizeof(path), "/proc/%d/cmdline", pid) < 0) 18335c4bbdfSmrg return; 18435c4bbdfSmrg fd = open(path, O_RDONLY); 18535c4bbdfSmrg if (fd < 0) 1861b5d61b8Smrg#ifdef __sun 1871b5d61b8Smrg goto fallback; 1881b5d61b8Smrg#else 18935c4bbdfSmrg return; 1901b5d61b8Smrg#endif 19135c4bbdfSmrg 19235c4bbdfSmrg /* Read the contents of /proc/pid/cmdline. It should contain the 19335c4bbdfSmrg * process name and arguments. */ 19435c4bbdfSmrg totsize = read(fd, path, sizeof(path)); 19535c4bbdfSmrg close(fd); 19635c4bbdfSmrg if (totsize <= 0) 19735c4bbdfSmrg return; 19835c4bbdfSmrg path[totsize - 1] = '\0'; 19935c4bbdfSmrg 20035c4bbdfSmrg /* Contruct the process name without arguments. */ 20135c4bbdfSmrg if (cmdname) { 20235c4bbdfSmrg *cmdname = strdup(path); 20335c4bbdfSmrg } 20435c4bbdfSmrg 20535c4bbdfSmrg /* Construct the arguments for client process. */ 20635c4bbdfSmrg if (cmdargs) { 20735c4bbdfSmrg int cmdsize = strlen(path) + 1; 20835c4bbdfSmrg int argsize = totsize - cmdsize; 20935c4bbdfSmrg char *args = NULL; 21035c4bbdfSmrg 21135c4bbdfSmrg if (argsize > 0) 21235c4bbdfSmrg args = malloc(argsize); 21335c4bbdfSmrg if (args) { 21435c4bbdfSmrg int i = 0; 21535c4bbdfSmrg 21635c4bbdfSmrg for (i = 0; i < (argsize - 1); ++i) { 21735c4bbdfSmrg const char c = path[cmdsize + i]; 21835c4bbdfSmrg 21935c4bbdfSmrg args[i] = (c == '\0') ? ' ' : c; 22035c4bbdfSmrg } 22135c4bbdfSmrg args[argsize - 1] = '\0'; 22235c4bbdfSmrg *cmdargs = args; 22335c4bbdfSmrg } 22435c4bbdfSmrg } 2251b5d61b8Smrg return; 2261b5d61b8Smrg#endif 2271b5d61b8Smrg 2281b5d61b8Smrg#ifdef __sun /* Solaris */ 2291b5d61b8Smrg fallback: 2301b5d61b8Smrg /* Solaris prior to 11.3.5 does not support /proc/pid/cmdline, but 2311b5d61b8Smrg * makes information similar to what ps shows available in a binary 2321b5d61b8Smrg * structure in the /proc/pid/psinfo file. */ 2331b5d61b8Smrg if (snprintf(path, sizeof(path), "/proc/%d/psinfo", pid) < 0) 2341b5d61b8Smrg return; 2351b5d61b8Smrg fd = open(path, O_RDONLY); 2361b5d61b8Smrg if (fd < 0) { 2371b5d61b8Smrg ErrorF("Failed to open %s: %s\n", path, strerror(errno)); 2381b5d61b8Smrg return; 2391b5d61b8Smrg } 2401b5d61b8Smrg else { 2411b5d61b8Smrg psinfo_t psinfo = { 0 }; 2421b5d61b8Smrg char *sp; 2431b5d61b8Smrg 2441b5d61b8Smrg totsize = read(fd, &psinfo, sizeof(psinfo_t)); 2451b5d61b8Smrg close(fd); 2461b5d61b8Smrg if (totsize <= 0) 2471b5d61b8Smrg return; 2481b5d61b8Smrg 2491b5d61b8Smrg /* pr_psargs is the first PRARGSZ (80) characters of the command 2501b5d61b8Smrg * line string - assume up to the first space is the command name, 2511b5d61b8Smrg * since it's not delimited. While there is also pr_fname, that's 2521b5d61b8Smrg * more limited, giving only the first 16 chars of the basename of 2531b5d61b8Smrg * the file that was exec'ed, thus cutting off many long gnome 2541b5d61b8Smrg * command names, or returning "isapython2.6" for all python scripts. 2551b5d61b8Smrg */ 2561b5d61b8Smrg psinfo.pr_psargs[PRARGSZ - 1] = '\0'; 2571b5d61b8Smrg sp = strchr(psinfo.pr_psargs, ' '); 2581b5d61b8Smrg if (sp) 2591b5d61b8Smrg *sp++ = '\0'; 2601b5d61b8Smrg 2611b5d61b8Smrg if (cmdname) 2621b5d61b8Smrg *cmdname = strdup(psinfo.pr_psargs); 2631b5d61b8Smrg 2641b5d61b8Smrg if (cmdargs && sp) 2651b5d61b8Smrg *cmdargs = strdup(sp); 2661b5d61b8Smrg } 26735c4bbdfSmrg#endif 26835c4bbdfSmrg} 26935c4bbdfSmrg 27035c4bbdfSmrg/** 27135c4bbdfSmrg * Called when a new client connects. Allocates client ID information. 27235c4bbdfSmrg * 27335c4bbdfSmrg * @param[in] client Recently connected client. 27435c4bbdfSmrg */ 27535c4bbdfSmrgvoid 27635c4bbdfSmrgReserveClientIds(struct _Client *client) 27735c4bbdfSmrg{ 27835c4bbdfSmrg#ifdef CLIENTIDS 27935c4bbdfSmrg if (client == NullClient) 28035c4bbdfSmrg return; 28135c4bbdfSmrg 28235c4bbdfSmrg assert(!client->clientIds); 28335c4bbdfSmrg client->clientIds = calloc(1, sizeof(ClientIdRec)); 28435c4bbdfSmrg if (!client->clientIds) 28535c4bbdfSmrg return; 28635c4bbdfSmrg 28735c4bbdfSmrg client->clientIds->pid = DetermineClientPid(client); 28835c4bbdfSmrg if (client->clientIds->pid != -1) 28935c4bbdfSmrg DetermineClientCmd(client->clientIds->pid, &client->clientIds->cmdname, 29035c4bbdfSmrg &client->clientIds->cmdargs); 29135c4bbdfSmrg 29235c4bbdfSmrg DebugF("client(%lx): Reserved pid(%d).\n", 29335c4bbdfSmrg (unsigned long) client->clientAsMask, client->clientIds->pid); 29435c4bbdfSmrg DebugF("client(%lx): Reserved cmdname(%s) and cmdargs(%s).\n", 29535c4bbdfSmrg (unsigned long) client->clientAsMask, 29635c4bbdfSmrg client->clientIds->cmdname ? client->clientIds->cmdname : "NULL", 29735c4bbdfSmrg client->clientIds->cmdargs ? client->clientIds->cmdargs : "NULL"); 29835c4bbdfSmrg#endif /* CLIENTIDS */ 29935c4bbdfSmrg} 30035c4bbdfSmrg 30135c4bbdfSmrg/** 30235c4bbdfSmrg * Called when an existing client disconnects. Frees client ID 30335c4bbdfSmrg * information. 30435c4bbdfSmrg * 30535c4bbdfSmrg * @param[in] client Recently disconnected client. 30635c4bbdfSmrg */ 30735c4bbdfSmrgvoid 30835c4bbdfSmrgReleaseClientIds(struct _Client *client) 30935c4bbdfSmrg{ 31035c4bbdfSmrg#ifdef CLIENTIDS 31135c4bbdfSmrg if (client == NullClient) 31235c4bbdfSmrg return; 31335c4bbdfSmrg 31435c4bbdfSmrg if (!client->clientIds) 31535c4bbdfSmrg return; 31635c4bbdfSmrg 31735c4bbdfSmrg DebugF("client(%lx): Released pid(%d).\n", 31835c4bbdfSmrg (unsigned long) client->clientAsMask, client->clientIds->pid); 31935c4bbdfSmrg DebugF("client(%lx): Released cmdline(%s) and cmdargs(%s).\n", 32035c4bbdfSmrg (unsigned long) client->clientAsMask, 32135c4bbdfSmrg client->clientIds->cmdname ? client->clientIds->cmdname : "NULL", 32235c4bbdfSmrg client->clientIds->cmdargs ? client->clientIds->cmdargs : "NULL"); 32335c4bbdfSmrg 32435c4bbdfSmrg free((void *) client->clientIds->cmdname); /* const char * */ 32535c4bbdfSmrg free((void *) client->clientIds->cmdargs); /* const char * */ 32635c4bbdfSmrg free(client->clientIds); 32735c4bbdfSmrg client->clientIds = NULL; 32835c4bbdfSmrg#endif /* CLIENTIDS */ 32935c4bbdfSmrg} 33035c4bbdfSmrg 33135c4bbdfSmrg/** 33235c4bbdfSmrg * Get cached PID of a client. 33335c4bbdfSmrg * 33435c4bbdfSmrg * param[in] client Client whose PID has been already cached. 33535c4bbdfSmrg * 33635c4bbdfSmrg * @return Cached client PID. Error (-1) if called: 33735c4bbdfSmrg * - before ClientStateInitial client state notification 33835c4bbdfSmrg * - after ClientStateGone client state notification 33935c4bbdfSmrg * - for remote clients 34035c4bbdfSmrg * 34135c4bbdfSmrg * @see DetermineClientPid 34235c4bbdfSmrg */ 34335c4bbdfSmrgpid_t 34435c4bbdfSmrgGetClientPid(struct _Client *client) 34535c4bbdfSmrg{ 34635c4bbdfSmrg if (client == NullClient) 34735c4bbdfSmrg return -1; 34835c4bbdfSmrg 34935c4bbdfSmrg if (!client->clientIds) 35035c4bbdfSmrg return -1; 35135c4bbdfSmrg 35235c4bbdfSmrg return client->clientIds->pid; 35335c4bbdfSmrg} 35435c4bbdfSmrg 35535c4bbdfSmrg/** 35635c4bbdfSmrg * Get cached command name string of a client. 35735c4bbdfSmrg * 35835c4bbdfSmrg * param[in] client Client whose command line string has been already 35935c4bbdfSmrg * cached. 36035c4bbdfSmrg * 36135c4bbdfSmrg * @return Cached client command name. Error (NULL) if called: 36235c4bbdfSmrg * - before ClientStateInitial client state notification 36335c4bbdfSmrg * - after ClientStateGone client state notification 36435c4bbdfSmrg * - for remote clients 36535c4bbdfSmrg * - on OS that doesn't support mapping of PID to command line 36635c4bbdfSmrg * 36735c4bbdfSmrg * @see DetermineClientCmd 36835c4bbdfSmrg */ 36935c4bbdfSmrgconst char * 37035c4bbdfSmrgGetClientCmdName(struct _Client *client) 37135c4bbdfSmrg{ 37235c4bbdfSmrg if (client == NullClient) 37335c4bbdfSmrg return NULL; 37435c4bbdfSmrg 37535c4bbdfSmrg if (!client->clientIds) 37635c4bbdfSmrg return NULL; 37735c4bbdfSmrg 37835c4bbdfSmrg return client->clientIds->cmdname; 37935c4bbdfSmrg} 38035c4bbdfSmrg 38135c4bbdfSmrg/** 38235c4bbdfSmrg * Get cached command arguments string of a client. 38335c4bbdfSmrg * 38435c4bbdfSmrg * param[in] client Client whose command line string has been already 38535c4bbdfSmrg * cached. 38635c4bbdfSmrg * 38735c4bbdfSmrg * @return Cached client command arguments. Error (NULL) if called: 38835c4bbdfSmrg * - before ClientStateInitial client state notification 38935c4bbdfSmrg * - after ClientStateGone client state notification 39035c4bbdfSmrg * - for remote clients 39135c4bbdfSmrg * - on OS that doesn't support mapping of PID to command line 39235c4bbdfSmrg * 39335c4bbdfSmrg * @see DetermineClientCmd 39435c4bbdfSmrg */ 39535c4bbdfSmrgconst char * 39635c4bbdfSmrgGetClientCmdArgs(struct _Client *client) 39735c4bbdfSmrg{ 39835c4bbdfSmrg if (client == NullClient) 39935c4bbdfSmrg return NULL; 40035c4bbdfSmrg 40135c4bbdfSmrg if (!client->clientIds) 40235c4bbdfSmrg return NULL; 40335c4bbdfSmrg 40435c4bbdfSmrg return client->clientIds->cmdargs; 40535c4bbdfSmrg} 406