client.c revision 35c4bbdf
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 14535c4bbdfSmrg#ifdef __sun /* Solaris */ 14635c4bbdfSmrg /* Solaris does not support /proc/pid/cmdline, but makes information 14735c4bbdfSmrg * similar to what ps shows available in a binary structure in the 14835c4bbdfSmrg * /proc/pid/psinfo file. */ 14935c4bbdfSmrg if (snprintf(path, sizeof(path), "/proc/%d/psinfo", pid) < 0) 15035c4bbdfSmrg return; 15135c4bbdfSmrg fd = open(path, O_RDONLY); 15235c4bbdfSmrg if (fd < 0) { 15335c4bbdfSmrg ErrorF("Failed to open %s: %s\n", path, strerror(errno)); 15435c4bbdfSmrg return; 15535c4bbdfSmrg } 15635c4bbdfSmrg else { 15735c4bbdfSmrg psinfo_t psinfo = { 0 }; 15835c4bbdfSmrg char *sp; 15935c4bbdfSmrg 16035c4bbdfSmrg totsize = read(fd, &psinfo, sizeof(psinfo_t)); 16135c4bbdfSmrg close(fd); 16235c4bbdfSmrg if (totsize <= 0) 16335c4bbdfSmrg return; 16435c4bbdfSmrg 16535c4bbdfSmrg /* pr_psargs is the first PRARGSZ (80) characters of the command 16635c4bbdfSmrg * line string - assume up to the first space is the command name, 16735c4bbdfSmrg * since it's not delimited. While there is also pr_fname, that's 16835c4bbdfSmrg * more limited, giving only the first 16 chars of the basename of 16935c4bbdfSmrg * the file that was exec'ed, thus cutting off many long gnome 17035c4bbdfSmrg * command names, or returning "isapython2.6" for all python scripts. 17135c4bbdfSmrg */ 17235c4bbdfSmrg psinfo.pr_psargs[PRARGSZ - 1] = '\0'; 17335c4bbdfSmrg sp = strchr(psinfo.pr_psargs, ' '); 17435c4bbdfSmrg if (sp) 17535c4bbdfSmrg *sp++ = '\0'; 17635c4bbdfSmrg 17735c4bbdfSmrg if (cmdname) 17835c4bbdfSmrg *cmdname = strdup(psinfo.pr_psargs); 17935c4bbdfSmrg 18035c4bbdfSmrg if (cmdargs && sp) 18135c4bbdfSmrg *cmdargs = strdup(sp); 18235c4bbdfSmrg } 18335c4bbdfSmrg#elif defined(__OpenBSD__) 18435c4bbdfSmrg /* on OpenBSD use kvm_getargv() */ 18535c4bbdfSmrg { 18635c4bbdfSmrg kvm_t *kd; 18735c4bbdfSmrg char errbuf[_POSIX2_LINE_MAX]; 18835c4bbdfSmrg char **argv; 18935c4bbdfSmrg struct kinfo_proc *kp; 19035c4bbdfSmrg size_t len = 0; 19135c4bbdfSmrg int i, n; 19235c4bbdfSmrg 19335c4bbdfSmrg kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 19435c4bbdfSmrg if (kd == NULL) 19535c4bbdfSmrg return; 19635c4bbdfSmrg kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(struct kinfo_proc), 19735c4bbdfSmrg &n); 19835c4bbdfSmrg if (n != 1) 19935c4bbdfSmrg return; 20035c4bbdfSmrg argv = kvm_getargv(kd, kp, 0); 20135c4bbdfSmrg *cmdname = strdup(argv[0]); 20235c4bbdfSmrg i = 1; 20335c4bbdfSmrg while (argv[i] != NULL) { 20435c4bbdfSmrg len += strlen(argv[i]) + 1; 20535c4bbdfSmrg i++; 20635c4bbdfSmrg } 20735c4bbdfSmrg *cmdargs = calloc(1, len); 20835c4bbdfSmrg i = 1; 20935c4bbdfSmrg while (argv[i] != NULL) { 21035c4bbdfSmrg strlcat(*cmdargs, argv[i], len); 21135c4bbdfSmrg strlcat(*cmdargs, " ", len); 21235c4bbdfSmrg i++; 21335c4bbdfSmrg } 21435c4bbdfSmrg kvm_close(kd); 21535c4bbdfSmrg } 21635c4bbdfSmrg#else /* Linux using /proc/pid/cmdline */ 21735c4bbdfSmrg 21835c4bbdfSmrg /* Check if /proc/pid/cmdline exists. It's not supported on all 21935c4bbdfSmrg * operating systems. */ 22035c4bbdfSmrg if (snprintf(path, sizeof(path), "/proc/%d/cmdline", pid) < 0) 22135c4bbdfSmrg return; 22235c4bbdfSmrg fd = open(path, O_RDONLY); 22335c4bbdfSmrg if (fd < 0) 22435c4bbdfSmrg return; 22535c4bbdfSmrg 22635c4bbdfSmrg /* Read the contents of /proc/pid/cmdline. It should contain the 22735c4bbdfSmrg * process name and arguments. */ 22835c4bbdfSmrg totsize = read(fd, path, sizeof(path)); 22935c4bbdfSmrg close(fd); 23035c4bbdfSmrg if (totsize <= 0) 23135c4bbdfSmrg return; 23235c4bbdfSmrg path[totsize - 1] = '\0'; 23335c4bbdfSmrg 23435c4bbdfSmrg /* Contruct the process name without arguments. */ 23535c4bbdfSmrg if (cmdname) { 23635c4bbdfSmrg *cmdname = strdup(path); 23735c4bbdfSmrg } 23835c4bbdfSmrg 23935c4bbdfSmrg /* Construct the arguments for client process. */ 24035c4bbdfSmrg if (cmdargs) { 24135c4bbdfSmrg int cmdsize = strlen(path) + 1; 24235c4bbdfSmrg int argsize = totsize - cmdsize; 24335c4bbdfSmrg char *args = NULL; 24435c4bbdfSmrg 24535c4bbdfSmrg if (argsize > 0) 24635c4bbdfSmrg args = malloc(argsize); 24735c4bbdfSmrg if (args) { 24835c4bbdfSmrg int i = 0; 24935c4bbdfSmrg 25035c4bbdfSmrg for (i = 0; i < (argsize - 1); ++i) { 25135c4bbdfSmrg const char c = path[cmdsize + i]; 25235c4bbdfSmrg 25335c4bbdfSmrg args[i] = (c == '\0') ? ' ' : c; 25435c4bbdfSmrg } 25535c4bbdfSmrg args[argsize - 1] = '\0'; 25635c4bbdfSmrg *cmdargs = args; 25735c4bbdfSmrg } 25835c4bbdfSmrg } 25935c4bbdfSmrg#endif 26035c4bbdfSmrg} 26135c4bbdfSmrg 26235c4bbdfSmrg/** 26335c4bbdfSmrg * Called when a new client connects. Allocates client ID information. 26435c4bbdfSmrg * 26535c4bbdfSmrg * @param[in] client Recently connected client. 26635c4bbdfSmrg */ 26735c4bbdfSmrgvoid 26835c4bbdfSmrgReserveClientIds(struct _Client *client) 26935c4bbdfSmrg{ 27035c4bbdfSmrg#ifdef CLIENTIDS 27135c4bbdfSmrg if (client == NullClient) 27235c4bbdfSmrg return; 27335c4bbdfSmrg 27435c4bbdfSmrg assert(!client->clientIds); 27535c4bbdfSmrg client->clientIds = calloc(1, sizeof(ClientIdRec)); 27635c4bbdfSmrg if (!client->clientIds) 27735c4bbdfSmrg return; 27835c4bbdfSmrg 27935c4bbdfSmrg client->clientIds->pid = DetermineClientPid(client); 28035c4bbdfSmrg if (client->clientIds->pid != -1) 28135c4bbdfSmrg DetermineClientCmd(client->clientIds->pid, &client->clientIds->cmdname, 28235c4bbdfSmrg &client->clientIds->cmdargs); 28335c4bbdfSmrg 28435c4bbdfSmrg DebugF("client(%lx): Reserved pid(%d).\n", 28535c4bbdfSmrg (unsigned long) client->clientAsMask, client->clientIds->pid); 28635c4bbdfSmrg DebugF("client(%lx): Reserved cmdname(%s) and cmdargs(%s).\n", 28735c4bbdfSmrg (unsigned long) client->clientAsMask, 28835c4bbdfSmrg client->clientIds->cmdname ? client->clientIds->cmdname : "NULL", 28935c4bbdfSmrg client->clientIds->cmdargs ? client->clientIds->cmdargs : "NULL"); 29035c4bbdfSmrg#endif /* CLIENTIDS */ 29135c4bbdfSmrg} 29235c4bbdfSmrg 29335c4bbdfSmrg/** 29435c4bbdfSmrg * Called when an existing client disconnects. Frees client ID 29535c4bbdfSmrg * information. 29635c4bbdfSmrg * 29735c4bbdfSmrg * @param[in] client Recently disconnected client. 29835c4bbdfSmrg */ 29935c4bbdfSmrgvoid 30035c4bbdfSmrgReleaseClientIds(struct _Client *client) 30135c4bbdfSmrg{ 30235c4bbdfSmrg#ifdef CLIENTIDS 30335c4bbdfSmrg if (client == NullClient) 30435c4bbdfSmrg return; 30535c4bbdfSmrg 30635c4bbdfSmrg if (!client->clientIds) 30735c4bbdfSmrg return; 30835c4bbdfSmrg 30935c4bbdfSmrg DebugF("client(%lx): Released pid(%d).\n", 31035c4bbdfSmrg (unsigned long) client->clientAsMask, client->clientIds->pid); 31135c4bbdfSmrg DebugF("client(%lx): Released cmdline(%s) and cmdargs(%s).\n", 31235c4bbdfSmrg (unsigned long) client->clientAsMask, 31335c4bbdfSmrg client->clientIds->cmdname ? client->clientIds->cmdname : "NULL", 31435c4bbdfSmrg client->clientIds->cmdargs ? client->clientIds->cmdargs : "NULL"); 31535c4bbdfSmrg 31635c4bbdfSmrg free((void *) client->clientIds->cmdname); /* const char * */ 31735c4bbdfSmrg free((void *) client->clientIds->cmdargs); /* const char * */ 31835c4bbdfSmrg free(client->clientIds); 31935c4bbdfSmrg client->clientIds = NULL; 32035c4bbdfSmrg#endif /* CLIENTIDS */ 32135c4bbdfSmrg} 32235c4bbdfSmrg 32335c4bbdfSmrg/** 32435c4bbdfSmrg * Get cached PID of a client. 32535c4bbdfSmrg * 32635c4bbdfSmrg * param[in] client Client whose PID has been already cached. 32735c4bbdfSmrg * 32835c4bbdfSmrg * @return Cached client PID. Error (-1) if called: 32935c4bbdfSmrg * - before ClientStateInitial client state notification 33035c4bbdfSmrg * - after ClientStateGone client state notification 33135c4bbdfSmrg * - for remote clients 33235c4bbdfSmrg * 33335c4bbdfSmrg * @see DetermineClientPid 33435c4bbdfSmrg */ 33535c4bbdfSmrgpid_t 33635c4bbdfSmrgGetClientPid(struct _Client *client) 33735c4bbdfSmrg{ 33835c4bbdfSmrg if (client == NullClient) 33935c4bbdfSmrg return -1; 34035c4bbdfSmrg 34135c4bbdfSmrg if (!client->clientIds) 34235c4bbdfSmrg return -1; 34335c4bbdfSmrg 34435c4bbdfSmrg return client->clientIds->pid; 34535c4bbdfSmrg} 34635c4bbdfSmrg 34735c4bbdfSmrg/** 34835c4bbdfSmrg * Get cached command name string of a client. 34935c4bbdfSmrg * 35035c4bbdfSmrg * param[in] client Client whose command line string has been already 35135c4bbdfSmrg * cached. 35235c4bbdfSmrg * 35335c4bbdfSmrg * @return Cached client command name. Error (NULL) if called: 35435c4bbdfSmrg * - before ClientStateInitial client state notification 35535c4bbdfSmrg * - after ClientStateGone client state notification 35635c4bbdfSmrg * - for remote clients 35735c4bbdfSmrg * - on OS that doesn't support mapping of PID to command line 35835c4bbdfSmrg * 35935c4bbdfSmrg * @see DetermineClientCmd 36035c4bbdfSmrg */ 36135c4bbdfSmrgconst char * 36235c4bbdfSmrgGetClientCmdName(struct _Client *client) 36335c4bbdfSmrg{ 36435c4bbdfSmrg if (client == NullClient) 36535c4bbdfSmrg return NULL; 36635c4bbdfSmrg 36735c4bbdfSmrg if (!client->clientIds) 36835c4bbdfSmrg return NULL; 36935c4bbdfSmrg 37035c4bbdfSmrg return client->clientIds->cmdname; 37135c4bbdfSmrg} 37235c4bbdfSmrg 37335c4bbdfSmrg/** 37435c4bbdfSmrg * Get cached command arguments string of a client. 37535c4bbdfSmrg * 37635c4bbdfSmrg * param[in] client Client whose command line string has been already 37735c4bbdfSmrg * cached. 37835c4bbdfSmrg * 37935c4bbdfSmrg * @return Cached client command arguments. Error (NULL) if called: 38035c4bbdfSmrg * - before ClientStateInitial client state notification 38135c4bbdfSmrg * - after ClientStateGone client state notification 38235c4bbdfSmrg * - for remote clients 38335c4bbdfSmrg * - on OS that doesn't support mapping of PID to command line 38435c4bbdfSmrg * 38535c4bbdfSmrg * @see DetermineClientCmd 38635c4bbdfSmrg */ 38735c4bbdfSmrgconst char * 38835c4bbdfSmrgGetClientCmdArgs(struct _Client *client) 38935c4bbdfSmrg{ 39035c4bbdfSmrg if (client == NullClient) 39135c4bbdfSmrg return NULL; 39235c4bbdfSmrg 39335c4bbdfSmrg if (!client->clientIds) 39435c4bbdfSmrg return NULL; 39535c4bbdfSmrg 39635c4bbdfSmrg return client->clientIds->cmdargs; 39735c4bbdfSmrg} 398