You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
464 lines
15 KiB
464 lines
15 KiB
/*
|
|
* Copyright (c) 1983 Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or
|
|
* without modification, are permitted provided that the following
|
|
* conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials
|
|
* provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* (BSD License, from tdelibs/doc/common/bsd-license.html)
|
|
*/
|
|
|
|
#include "includ.h"
|
|
#include <sys/param.h>
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#include <netdb.h>
|
|
#include <syslog.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#define USE_UT_HOST
|
|
#ifndef UT_HOSTSIZE
|
|
#define UT_HOSTSIZE 12 /*whatever*/
|
|
#undef USE_UT_HOST
|
|
#endif
|
|
|
|
#include <pwd.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <fstream>
|
|
|
|
#ifdef ALL_PROCESSES_AND_PROC_FIND_USER
|
|
#include <dirent.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
#include "proto.h"
|
|
#include "defs.h"
|
|
|
|
#ifdef PROC_FIND_USER
|
|
|
|
#define DISPLAYS_LIST_MAX 200
|
|
#define DISPLAY_MAX 50
|
|
|
|
/* get DISPLAY variable of a process */
|
|
char *get_display(pid_t pid) {
|
|
static char buf[1024];
|
|
|
|
sprintf(buf, "/proc/%d/environ", pid);
|
|
|
|
std::ifstream ifstr(buf);
|
|
if (ifstr.fail()) return 0;
|
|
/* ktalk_debug("reading %s...", fname); */
|
|
|
|
char * dpy = 0;
|
|
while (!ifstr.eof()) {
|
|
ifstr.getline(buf, sizeof buf - 1, '\0');
|
|
if (ifstr.fail()) {
|
|
syslog(LOG_ERR, "getline: %s", strerror(errno));
|
|
return 0; /* read error */
|
|
}
|
|
if (!strncmp("DISPLAY=", buf, 8)) {
|
|
int l = strlen(buf + 8);
|
|
if( l >= DISPLAY_MAX )
|
|
return 0;
|
|
if (l >= 2 && l < (int) sizeof buf - 1) {
|
|
dpy = new char[ l+2 ];
|
|
strcpy(dpy," ");
|
|
strcat(dpy, buf + 8); /* returns " "+dpy */
|
|
/* ktalk_debug("- %s",dpy); */
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return dpy;
|
|
}
|
|
|
|
/* As utmp isn't reliable (neither xdm nor tdm logs into it ! :( ),
|
|
we have to look at processes directly. /proc helps a lot, under linux.
|
|
How to do it under other unixes ? */
|
|
#ifdef ALL_PROCESSES_AND_PROC_FIND_USER
|
|
|
|
/* awful global variable, but how to pass it to select_process() otherwise ?
|
|
scandir() doesn't allow this, of course... */
|
|
unsigned int user_uid;
|
|
|
|
/* selection function used by scandir */
|
|
int select_process(
|
|
#ifdef SCANDIR_NEEDS_CONST
|
|
const struct dirent *direntry
|
|
#else
|
|
struct dirent *direntry
|
|
#endif
|
|
)
|
|
{
|
|
/* returns 1 if username owns <direntry> */
|
|
struct stat statbuf;
|
|
|
|
/* make absolute path (would sprintf be better ?)*/
|
|
char abspath[20]="/proc/";
|
|
if( strlen( direntry->d_name ) > 12 )
|
|
return 0;
|
|
strcat(abspath,direntry->d_name);
|
|
|
|
if (isdigit(direntry->d_name[0])) { /* starts with [0-9]*/
|
|
if (!stat(abspath, &statbuf)) { /* if exists */
|
|
if (S_ISDIR(statbuf.st_mode)) { /* and is a directory and */
|
|
if (statbuf.st_uid == user_uid) /* is owned by user_uid*/
|
|
{
|
|
/* We have to force errno=0, because otherwise, scandir will stop ! */
|
|
/* the problem is that glibc sets errno in getpwnam and syslog
|
|
(glibc-2.0.5c/6, with libstdc++ 2.7.2) */
|
|
errno=0;
|
|
return 1;
|
|
}
|
|
/* else ktalk_debug("st_uid=%d", statbuf.st_uid); */
|
|
} /* else ktalk_debug("st_mode=%d", statbuf.st_mode); */
|
|
} else ktalk_debug("stat error : %s", strerror(errno));
|
|
}
|
|
|
|
errno=0;
|
|
return 0;
|
|
}
|
|
|
|
/* scan /proc for any process owned by 'name'.
|
|
If DISPLAY is set, set 'disp'.
|
|
|
|
Called only if no X utmp entry found. */
|
|
|
|
/*
|
|
* Major memory leak, never frees the memory allocated by scandir
|
|
* (why not use readdir() in the first place?
|
|
* Major buffer overflow: If I set my DISPLAY variable to a
|
|
* 1024 bytes string, it'll overflow displays_list.
|
|
*/
|
|
|
|
int find_X_process(char *name, char *disp) {
|
|
char displays_list[DISPLAYS_LIST_MAX] = " "; /* yes, one space */
|
|
char * dispwithblanks;
|
|
struct dirent **namelist;
|
|
|
|
struct passwd * pw = getpwnam(name);
|
|
ktalk_debug("find_X_process");
|
|
/* find uid */
|
|
if ((pw) && ((user_uid=pw->pw_uid)>10))
|
|
{ /* uid<10 : no X detection because any suid program will be taken
|
|
as owned by root, not by its real owner */
|
|
/* scan /proc */
|
|
int n = scandir("/proc", &namelist, select_process, 0 /*no sort*/);
|
|
if (n < 0)
|
|
ktalk_debug("scandir: %s", strerror(errno));
|
|
else
|
|
while(n--)
|
|
{
|
|
/* find DISPLAY */
|
|
dispwithblanks = get_display(atoi(namelist[n]->d_name));
|
|
if (dispwithblanks) {
|
|
/* This way, if :0.0 is in the list, :0 is not inserted */
|
|
/* XXX Yes, but if I have two displays, one foo:0.0
|
|
* and one bar:0.0, then bar:0 is not caught by the
|
|
* check below. But this is just peanuts.
|
|
*/
|
|
if (!strstr(displays_list,dispwithblanks))
|
|
{ /* not already in the list? */
|
|
char * pointlocation=strstr(dispwithblanks,".");
|
|
if (pointlocation) *pointlocation='\0';
|
|
if (!strstr(displays_list,dispwithblanks)
|
|
&& strlen(dispwithblanks)+strlen(displays_list)<DISPLAYS_LIST_MAX)
|
|
{ /* display up to the '.' mustn't be already in the list */
|
|
strcat(displays_list,dispwithblanks+1); /* insert display (no ' ') */
|
|
strcat(displays_list," "); /* and a blank */
|
|
}
|
|
} /* if strtsr */
|
|
delete dispwithblanks;
|
|
} /* if dispwithblanks */
|
|
} /* while */
|
|
if (strlen(displays_list)>1)
|
|
{
|
|
strcpy(disp,displays_list+1); /* removes the leading white space
|
|
but leave the final one, needed by announce.c */
|
|
return 1;
|
|
}
|
|
} /* if pw */
|
|
return 0;
|
|
}
|
|
|
|
#endif /* ALL_PROCESSES_AND_PROC_FIND_USER */
|
|
|
|
#ifdef UTMP_AND_PROC_FIND_USER
|
|
|
|
/*
|
|
* Search utmp for the local user
|
|
*
|
|
* Priorities:
|
|
* login from xdm
|
|
* login from pseudo terminal with $DISPLAY set
|
|
* login from pseudo terminal
|
|
* other login
|
|
*/
|
|
#define PRIO_LOGIN 1 /* user is logged in */
|
|
#define PRIO_PTY 2 /* user is logged in on a
|
|
pseudo terminal */
|
|
#define PRIO_DISPLAY 3 /* user is logged in on a
|
|
pseudo terminal and has
|
|
$DISPLAY set. */
|
|
#define PRIO_XDM 4 /* user is logged in from xdm */
|
|
|
|
#define SCMPN(a, b) strncmp(a, b, sizeof (a))
|
|
|
|
int find_user(char *name, char *tty, char *disp) {
|
|
struct utmp *ubuf;
|
|
int prio = 0, status = NOT_HERE;
|
|
struct stat statb;
|
|
char ftty[20+UT_LINESIZE];
|
|
char *ntty, *dpy;
|
|
char ttyFound[UT_LINESIZE] = "";
|
|
char dispFound[DISPLAYS_LIST_MAX] = "";
|
|
|
|
strcpy(ftty, _PATH_DEV);
|
|
ntty = ftty + strlen(ftty);
|
|
setutent();
|
|
while ((ubuf = getutent())) {
|
|
if ((ubuf->ut_type == USER_PROCESS) &&
|
|
(!SCMPN(ubuf->ut_name, name))) {
|
|
|
|
if (*tty == '\0') { /* no particular tty was requested */
|
|
|
|
if (Options.XAnnounce && ubuf->ut_line[0] == ':') {
|
|
/* this is a XDM login (really?). GREAT! */
|
|
syslog(LOG_DEBUG, "XDM login: %s at %s", name, ubuf->ut_line);
|
|
status = SUCCESS;
|
|
if (prio < PRIO_XDM) {
|
|
strcpy(dispFound, ubuf->ut_line);
|
|
strcat(dispFound, " ");
|
|
prio = PRIO_XDM;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
strcpy(ntty, ubuf->ut_line);
|
|
if (stat(ftty, &statb) != 0 || (!(statb.st_mode & 020)))
|
|
{
|
|
ktalk_debug("Permission denied on %s", ntty);
|
|
continue; /* not a char dev */
|
|
}
|
|
|
|
/* device exists and is a character device */
|
|
status = SUCCESS;
|
|
if (Options.debug_mode) syslog(LOG_DEBUG, "Found %s at %s", name, ubuf->ut_line);
|
|
if (prio < PRIO_LOGIN) {
|
|
prio = PRIO_LOGIN;
|
|
strcpy(ttyFound, ubuf->ut_line);
|
|
*dispFound = '\0';
|
|
}
|
|
|
|
/* the following code is Linux specific...
|
|
* is there a portable way to
|
|
* 1) determine if a device is a pseudo terminal and
|
|
* 2) get environment variables of an arbitrary process?
|
|
*/
|
|
if (strncmp("tty", ubuf->ut_line, 3) != 0 ||
|
|
!strchr("pqrstuvwxyzabcde", ubuf->ut_line[3]))
|
|
continue; /* not a pty */
|
|
|
|
/* device is a pseudo terminal (ex : a xterm) */
|
|
if (Options.debug_mode) syslog(LOG_DEBUG, "PTY %s, ut_host=%s",
|
|
ubuf->ut_line, ubuf->ut_host);
|
|
if (prio < PRIO_PTY) {
|
|
prio = PRIO_PTY;
|
|
strcpy(ttyFound, ubuf->ut_line);
|
|
strcpy(dispFound, ubuf->ut_host);
|
|
strcat(dispFound, " ");
|
|
}
|
|
|
|
dpy = get_display(ubuf->ut_pid);
|
|
if (!dpy) continue; /* DISPLAY not set or empty */
|
|
|
|
/* $DISPLAY is set. */
|
|
if (Options.debug_mode) syslog(LOG_DEBUG, "Found display %s on %s",
|
|
dpy, ubuf->ut_line);
|
|
if (prio < PRIO_DISPLAY) {
|
|
prio = PRIO_DISPLAY;
|
|
strcpy(ttyFound, ubuf->ut_line);
|
|
strcpy(dispFound, dpy+1); /*no space*/
|
|
strcat(dispFound, " ");
|
|
}
|
|
delete dpy;
|
|
continue;
|
|
}
|
|
if (!strcmp(ubuf->ut_line, tty)) {
|
|
strcpy(ttyFound, ubuf->ut_line);
|
|
status = SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
endutent();
|
|
|
|
ktalk_debug("End of Utmp reading");
|
|
#if defined(HAVE_KDE) && defined(ALL_PROCESSES_AND_PROC_FIND_USER)
|
|
if (Options.XAnnounce && prio < PRIO_DISPLAY)
|
|
if (find_X_process(name, dispFound))
|
|
{ ktalk_debug(dispFound); status=SUCCESS; }
|
|
#endif
|
|
if (status == SUCCESS) {
|
|
(void) strcpy(tty, ttyFound);
|
|
(void) strcpy(disp, dispFound);
|
|
if (Options.debug_mode)
|
|
syslog(LOG_DEBUG, "Returning tty '%s', display '%s'", ttyFound, dispFound);
|
|
} else ktalk_debug("Returning status %d",status);
|
|
return (status);
|
|
}
|
|
|
|
#endif /*UTMP_AND_PROC_FIND_USER*/
|
|
|
|
#else /*not PROC_FIND_USER*/
|
|
|
|
#ifdef HAVE_UTMPX_H
|
|
|
|
int find_user(char *name, char *tty, char *disp) {
|
|
|
|
struct utmpx *ubuf;
|
|
int status;
|
|
struct stat statb;
|
|
char ftty[20 + sizeof ubuf->ut_line];
|
|
char ttyFound[sizeof ubuf->ut_line] = "";
|
|
char dispFound[sizeof ubuf->ut_line + 1] = "";
|
|
|
|
setutxent();
|
|
#define SCMPN(a, b) strncmp(a, b, sizeof (a))
|
|
status = NOT_HERE;
|
|
(void) strcpy(ftty, _PATH_DEV);
|
|
while ((ubuf = getutxent())) {
|
|
if ((ubuf->ut_type == USER_PROCESS) &&
|
|
(!SCMPN(ubuf->ut_user, name))) {
|
|
if (*tty == '\0') {
|
|
/* no particular tty was requested */
|
|
(void) strcpy(ftty+5, ubuf->ut_line);
|
|
if (stat(ftty,&statb) == 0) {
|
|
if (!(statb.st_mode & 020)) /* ?character device? */
|
|
continue;
|
|
(void) strcpy(ttyFound, ubuf->ut_line);
|
|
#ifdef USE_UT_HOST
|
|
(void) strcpy(dispFound, ubuf->ut_host);
|
|
strcat(dispFound, " ");
|
|
#endif
|
|
status = SUCCESS;
|
|
|
|
syslog(LOG_DEBUG, "%s", ttyFound);
|
|
if ((int) ttyFound[3] > (int) 'f') {
|
|
#ifdef USE_UT_HOST
|
|
if (Options.debug_mode) {
|
|
syslog(LOG_DEBUG, "I wanna this:%s", ttyFound);
|
|
syslog(LOG_DEBUG, "ut_host=%s", ubuf.ut_host);
|
|
syslog(LOG_DEBUG, "%s", ubuf.ut_line);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (!strcmp(ubuf->ut_line, tty)) {
|
|
status = SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
endutxent();
|
|
if (status == SUCCESS) {
|
|
(void) strcpy(tty, ttyFound);
|
|
(void) strcpy(disp, dispFound);
|
|
}
|
|
return (status);
|
|
}
|
|
|
|
#else /* HAVE_UTMPX_H */
|
|
|
|
int find_user(char *name, char *tty, char *disp) {
|
|
|
|
struct utmp ubuf;
|
|
int status;
|
|
FILE *fd;
|
|
struct stat statb;
|
|
char ftty[20+UT_LINESIZE];
|
|
char ttyFound[UT_LINESIZE] = "";
|
|
char dispFound[UT_HOSTSIZE+1] = "";
|
|
|
|
if (!(fd = fopen(_PATH_UTMP, "r"))) {
|
|
fprintf(stderr, "talkd: can't read %s.\n", _PATH_UTMP);
|
|
return (FAILED);
|
|
}
|
|
#define SCMPN(a, b) strncmp(a, b, sizeof (a))
|
|
status = NOT_HERE;
|
|
(void) strcpy(ftty, _PATH_DEV);
|
|
while (fread((char *) &ubuf, sizeof ubuf, 1, fd) == 1) {
|
|
if (!SCMPN(ubuf.ut_name, name)) {
|
|
if (*tty == '\0') {
|
|
/* no particular tty was requested */
|
|
(void) strcpy(ftty+5, ubuf.ut_line);
|
|
if (stat(ftty,&statb) == 0) {
|
|
if (!(statb.st_mode & 020)) /* ?character device? */
|
|
continue;
|
|
(void) strcpy(ttyFound, ubuf.ut_line);
|
|
#ifdef USE_UT_HOST
|
|
(void) strcpy(dispFound, ubuf.ut_host);
|
|
strcat(dispFound, " ");
|
|
#endif
|
|
status = SUCCESS;
|
|
|
|
syslog(LOG_DEBUG, "%s", ttyFound);
|
|
if ((int) ttyFound[3] > (int) 'f') {
|
|
#ifdef USE_UT_HOST
|
|
if (Options.debug_mode) {
|
|
syslog(LOG_DEBUG, "I wanna this:%s", ttyFound);
|
|
syslog(LOG_DEBUG, "ut_host=%s", ubuf.ut_host);
|
|
syslog(LOG_DEBUG, "%s", ubuf.ut_line);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (!strcmp(ubuf.ut_line, tty)) {
|
|
status = SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
fclose(fd);
|
|
if (status == SUCCESS) {
|
|
(void) strcpy(tty, ttyFound);
|
|
(void) strcpy(disp, dispFound);
|
|
}
|
|
return (status);
|
|
}
|
|
|
|
#endif /* HAVE_UTMPX_H */
|
|
|
|
#endif /*PROC_FIND_USER*/
|