/* Copyright (C) 1999 Beau Kuiper
hmmm, could be setuid root.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "ftpd.h"
int lockfd;
SHRMEMHEADER *shrmemptr;
#define FALSE 0
#define TRUE !FALSE
char *getipstr(unsigned int server)
{
static char ipstr[20];
snprintf(ipstr, 20, "%d.%d.%d.%d", server >> 24,
(server >> 16) & 0xFF,
(server >> 8) & 0xFF,
server & 0xFF);
return(ipstr);
}
int lockarea(int fd, int pos, int len, int locktype, int do_wait)
{
struct flock lock;
int lockt;
register int err;
lockt = (do_wait == TRUE ? F_SETLKW : F_SETLK);
lock.l_type = locktype;
lock.l_whence = SEEK_SET;
lock.l_start = pos;
lock.l_len = len;
err = fcntl(fd, lockt, &lock);
if (err == -1)
{
if (errno != EAGAIN)
ERRORMSGFATAL(strerror(errno));
else
return(FALSE);
}
return(TRUE);
}
void *shmem_connect(char *configfile)
{
int shmemnum;
key_t ipckey = ftok(configfile, '/');
lockfd = open(configfile, O_RDWR);
if (lockfd == -1)
ERRORMSGFATAL(safe_snprintf("Could not open lock file, reason: %s", strerror(errno)));
#ifdef DEBUG
printf("[shmat_init]\n");
#endif
shmemnum = shmget(ipckey, 0, 0);
if (shmemnum == -1)
return(NULL);
shrmemptr = shmat(shmemnum, NULL, 0600);
if ((int)shrmemptr == -1)
ERRORMSGFATAL(safe_snprintf("Could not connect to shared memory: %s", strerror(errno)));
/* get semaphores. Using the config file so others can join in
(we use file locks for portability, simplicity
and usability.) */
/* assume that if standalone isn't active, inetd is */
if (lockarea(lockfd, 3, 1, F_WRLCK, FALSE))
return(NULL);
if (shrmemptr->magic != CURRENTMAGIC)
ERRORMSGFATAL("Incorrect version!");
return(shrmemptr);
}
void pnums_showpid(void)
{
int pid;
lockarea(lockfd, 0, 1, F_WRLCK, TRUE);
pid = (int)shrmemptr->pid;
lockarea(lockfd, 0, 1, F_UNLCK, TRUE);
printf("%d\n", pid);
exit(0);
}
void pnums_count(char *username)
{
SHRMEMDATA *dpos;
SHRMEMHEADER *shdata;
int count;
shdata = mallocwrapper(SHMEMPROCSTART);
/* make a working copy of the stuff we want to display so
I can release the locks quickly */
lockarea(lockfd, 0, 3, F_WRLCK, TRUE);
memcpy(shdata, shrmemptr, sizeof(SHRMEMHEADER) + sizeof(SHRMEMDATA) *
(shrmemptr->numvserver + shrmemptr->numgroups));
lockarea(lockfd, 0, 3, F_UNLCK, TRUE);
dpos = SHRMEMDATPOS(shdata, 0);
if (username)
{
int done = FALSE;
for (count = 0; count < shdata->numvserver + shdata->numgroups; count++)
{
if (strncmp(dpos->name, username, MAXNAMELEN) == 0)
{
done = TRUE;
printf("%d %d\n", dpos->count, dpos->max);
}
dpos++;
}
if (!done)
printf("Group/virtual server not found!\n");
}
else
{
printf("User counts: (%d users, maximum %d users)\n", shdata->serverusercount,
shdata->servermaxcount);
if (shdata->numvserver > 0)
{
printf("Virtual Server Current Users Max Users\n");
for (count = 0; count < shdata->numvserver; count++)
{
printf("%-50s %-15d %d\n", dpos->name, dpos->count, dpos->max);
dpos++;
}
printf("\n");
}
printf("Group Current Users Max Users\n");
for (count = 0; count < shdata->numgroups; count++)
{
printf("%-50s %-15d %d\n", dpos->name, dpos->count, dpos->max);
dpos++;
}
printf("\n");
}
exit(0);
}
char **buildgrouplist(CONFIGFILECACHE *c, char **vserverlist)
{
char **temp_grouplist, **grouplist;
char **vserver, **group, **currentgroup;
int doadd, len;
/* this is cheating, but is easier */
grouplist = (char **)mallocwrapper(sizeof(char *) * c->sectioncount);
grouplist[0] = NULL;
vserver = vserverlist;
len = 0;
while(*vserver != NULL)
{
temp_grouplist = makeconfiglist(c, *vserver, "group");
currentgroup = temp_grouplist;
while(*currentgroup != NULL)
{
doadd = TRUE;
group = grouplist;
while (*group != NULL)
{
if (strcmp(*group, *currentgroup) == 0)
doadd = FALSE;
group++;
}
if (doadd)
{
*group = *currentgroup;
group++;
*group = NULL;
}
currentgroup++;
}
vserver++;
}
return(grouplist);
}
void inetd_count(CONFIGFILECACHE *c, char *username)
{
SCRFILEREC dat;
int count, pos, sectionid, count2;
int max, defmax, isvserver, found;
int usercount, *vcnt, *gcnt;
char **vserverlist, **grouplist;
/* build required information from config file */
sectionid = getsectionid(c, "main");
loadintfromconfig(c, sectionid, "maxusers", &defmax, MAXUSERS);
vserverlist = makeconfiglist(c, "main", "vserver");
if (vserverlist[0] == NULL)
grouplist = makeconfiglist(c, "main", "group");
else
grouplist = buildgrouplist(c, vserverlist);
/* check name lengths */
count = 0;
while (vserverlist[count] != NULL)
{
if (strlen(vserverlist[count]) >= MAXSECTIONLEN)
ERRORMSGFATAL(safe_snprintf("Vserver '%s', name too long.", vserverlist[count]));
count++;
}
count = 0;
while (grouplist[count] != NULL)
{
if (strlen(grouplist[count]) >= MAXSECTIONLEN)
ERRORMSGFATAL(safe_snprintf("Group '%s', name too long.", grouplist[count]));
count++;
}
if (username)
{
/* get information about part user asked for */
sectionid = getsectionid(c, username);
count = 0; isvserver = FALSE; found = FALSE;
while((vserverlist[count] != NULL) && (!isvserver))
{
if (strcmp(vserverlist[count], username) == 0)
isvserver = TRUE;
count++;
}
count = 0; found = isvserver;
while((grouplist[count] != NULL) && (!found))
{
if (strcmp(grouplist[count], username) == 0)
found = TRUE;
count++;
}
if ((sectionid == -1) || (!found))
ERRORMSGFATAL("Group/virtual server not found!\n");
loadintfromconfig(c, sectionid, "maxusers", &max, defmax);
lockarea(lockfd, 0, 1, F_WRLCK, TRUE);
lseek(lockfd, 0, SEEK_SET);
count = 0; pos = 0;
while(read(lockfd, &dat, sizeof(SCRFILEREC)) == sizeof(SCRFILEREC))
{
if (lockarea(lockfd, 10 + pos, 1, F_WRLCK, FALSE))
lockarea(lockfd, 10 + pos, 1, F_UNLCK, TRUE);
else
{
if (isvserver)
{
if (strcmp(username, dat.vserver) == 0)
count++;
}
else
{
if (strcmp(username, dat.groupname) == 0)
count++;
}
}
pos++;
}
lockarea(lockfd, 0, 1, F_UNLCK, TRUE);
printf("%d %d\n", count, max);
}
else
{
vcnt = (int *)mallocwrapper(sizeof(int) * c->sectioncount * 2);
gcnt = (int *)mallocwrapper(sizeof(int) * c->sectioncount * 2);
memset(vcnt, 0, c->sectioncount * sizeof(int)* 2);
memset(gcnt, 0, c->sectioncount * sizeof(int)* 2);
/* determine maximum users */
count2 = 0;
while(vserverlist[count2])
{
sectionid = getsectionid(c, vserverlist[count2]);
if (sectionid == -1)
ERRORMSGFATAL(safe_snprintf("could not find vserver section '%s'", vserverlist[count2]));
loadintfromconfig(c, sectionid, "maxusers",
vcnt + (c->sectioncount + count2), defmax);
count2++;
}
count2 = 0;
while(grouplist[count2])
{
sectionid = getsectionid(c, grouplist[count2]);
if (sectionid == -1)
ERRORMSGFATAL(safe_snprintf("could not find group section '%s'", vserverlist[count2]));
loadintfromconfig(c, sectionid, "maxusers",
gcnt + (c->sectioncount + count2), defmax);
count2++;
}
lockarea(lockfd, 0, 1, F_WRLCK, TRUE);
lseek(lockfd, 0, SEEK_SET);
count = 0; pos = 0; usercount = 0;
while(read(lockfd, &dat, sizeof(SCRFILEREC)) == sizeof(SCRFILEREC))
{
if (lockarea(lockfd, 10 + pos, 1, F_WRLCK, FALSE))
lockarea(lockfd, 10 + pos, 1, F_UNLCK, TRUE);
else
{
usercount++;
count2 = 0;
while(vserverlist[count2])
{
if (strcmp(vserverlist[count2], dat.vserver) == 0)
vcnt[count2]++;
count2++;
}
count2 = 0;
while(grouplist[count2])
{
if (strcmp(grouplist[count2], dat.groupname) == 0)
gcnt[count2]++;
count2++;
}
}
pos++;
}
lockarea(lockfd, 0, 1, F_UNLCK, TRUE);
printf("User counts: (%d users, maximum %d users)\n", usercount,
defmax);
if (vserverlist[0] != NULL)
{
printf("Virtual Server Current Users Max Users\n");
for (count = 0; vserverlist[count] != NULL; count++)
printf("%-50s %-15d %d\n", vserverlist[count], vcnt[count], vcnt[c->sectioncount + count]);
printf("\n");
}
printf("Group Current Users Max Users\n");
for (count = 0; grouplist[count]; count++)
if (gcnt[c->sectioncount + count] != 0)
printf("%-50s %-15d %d\n", grouplist[count], gcnt[count], gcnt[c->sectioncount + count]);
printf("\n");
}
exit(0);
}
void pnums_listdisplay(SCRMEMREC *newdat, SHRMEMHEADER *shdata, int useips)
{
char *usergroupstr = NULL;
SHRMEMDATA *dpos;
if (newdat->group == -1)
usergroupstr = safe_snprintf("not logged in", newdat->username);
else if (newdat->group == -2)
usergroupstr = safe_snprintf("%s/none", newdat->username);
else
{
dpos = SHRMEMDATPOS(shdata, newdat->group + shdata->numvserver);
usergroupstr = safe_snprintf("%s/%s", newdat->username, dpos->name);
}
if (useips)
printf("%-5d %-20s %-20s %s\n", newdat->pid,
usergroupstr, getipstr(newdat->ip), newdat->currentop);
else
printf("%-5d %-20s %-20s %s\n", newdat->pid,
usergroupstr, newdat->remotehost, newdat->currentop);
freewrapper(usergroupstr);
}
void inetd_listdisplay(STRING **s, SCRFILEREC *newdat, int useips)
{
char *usergroupstr = NULL;
if (strcmp(newdat->groupname, "none") == 0)
usergroupstr = strdupwrapper("not logged in");
else
usergroupstr = safe_snprintf("%s/%s", newdat->username, newdat->groupname);
if (useips)
string_catprintf(s, "%-5d %-20s %-20s %s\n", newdat->pid,
usergroupstr, getipstr(newdat->ip), newdat->currentop);
else
string_catprintf(s, "%-5d %-20s %-20s %s\n", newdat->pid,
usergroupstr, newdat->remotehost, newdat->currentop);
freewrapper(usergroupstr);
}
void pnums_list(char *username, int useips)
{
SCRMEMREC *dat, *newdat;
SHRMEMHEADER *shdata;
int count;
int count2 = 0;
shdata = mallocwrapper(SHMEMPROCSTART);
newdat = mallocwrapper(sizeof(SCRMEMREC));
lockarea(lockfd, 0, 3, F_WRLCK, TRUE);
memcpy(shdata, shrmemptr, sizeof(SHRMEMHEADER) + sizeof(SHRMEMDATA) *
(shrmemptr->numvserver + shrmemptr->numgroups));
lockarea(lockfd, 0, 3, F_UNLCK, TRUE);
dat = (SCRMEMREC *)((char *)shrmemptr + (SHMEMPROCSTART));
printf("Pid Username/Group Host Operation\n");
for (count = 0; count < shrmemptr->numrecs; count++)
{
lockarea(lockfd, 10+count, 1, F_WRLCK, TRUE);
memcpy(newdat, dat, sizeof(SCRMEMREC));
lockarea(lockfd, 10+count, 1, F_UNLCK, TRUE);
if (newdat->pid > 0)
{
if (username != NULL)
{
if ((strcmp(username, newdat->username) == 0)
&& (newdat->group != -1))
{
pnums_listdisplay(newdat, shdata, useips);
count2++;
}
}
else
{
pnums_listdisplay(newdat, shdata, useips);
count2++;
}
}
dat++;
}
printf("------- %d users.\n", count2);
}
void inetd_list(char *username, int useips)
{
SCRFILEREC dat;
/* I need to cache the output into memory instead of to the terminal
so that the master lock on the scratchfile is held as little as
possible! */
STRING *outstring = string_new();
int count;
int count2 = 0;
lockarea(lockfd, 0, 1, F_WRLCK, TRUE);
lseek(lockfd, 0, SEEK_SET);
string_catprintf(&outstring, "Pid Username/Group Host Operation\n");
count = 0;
while(read(lockfd, &dat, sizeof(SCRFILEREC)) == sizeof(SCRFILEREC))
{
if (lockarea(lockfd, 10 + count, 1, F_WRLCK, FALSE))
lockarea(lockfd, 10 + count, 1, F_UNLCK, TRUE);
else
{
if (username != NULL)
{
if (strcmp(username, dat.username) == 0)
{
inetd_listdisplay(&outstring, &dat, useips);
count2++;
}
}
else
{
inetd_listdisplay(&outstring, &dat, useips);
count2++;
}
}
count++;
}
lockarea(lockfd, 0, 1, F_UNLCK, TRUE);
string_catprintf(&outstring, "------- %d users.\n", count2);
/* now print the output */
printf("%s", STRTOCHAR(outstring));
}
void usage(char *name)
{
printf("ftpwho, shows who is logged in and what they are doing.\n\n");
printf("Usage: %s [-V][-h][-c configfile]\n\n", name);
printf(" -V Show version information.\n");
printf(" -h Show usage information.\n");
printf(" -p Get pid of "PROGNAME" deamon.\n");
printf(" -c configfile Specify config file "PROGNAME" is running as.\n");
printf(" -C Return counts of groups and vservers.\n");
printf(" -n Return IP's instead of hostnames.\n");
printf(" -u namespec Return logins of user 'namespec' or if\n");
printf(" -C is used, counts of specific group or vserver.\n\n");
exit(1);
}
int main(int argc, char **argv)
{
CONFIGFILECACHE *cfiledata;
int count = FALSE;
int do_getpid = FALSE;
int useips = FALSE;
char *username = NULL;
char *fconfig = NULL;
char *scratchfile = NULL;
int section, line, error;
void *sharea;
int ch;
int saved_uid = geteuid();
int saved_gid = getegid();
int extraperms = FALSE;
extern char *optarg;
if ((saved_uid != getuid()) || (saved_gid != getgid()))
extraperms = TRUE;
while((ch = getopt(argc, argv, "Vc:hu:Cnp")) != EOF)
{
switch(ch)
{
case 'V':
showversion("ftpwho");
case 'c':
fconfig = optarg;
break;
case 'u':
username = optarg;
break;
case 'C':
count = TRUE;
break;
case 'n':
useips = TRUE;
break;
case 'p':
do_getpid = TRUE;
break;
case 'h':
default:
usage(argv[0]);
}
}
if (fconfig == NULL)
fconfig = CONFIGFILE;
if ((do_getpid && count) || (do_getpid && username))
{
printf("invalid arguments.\n");
usage(argv[0]);
}
cfiledata = loadconfigcache(fconfig, &line, &error);
/* obtain scratchfile name */
if (cfiledata == NULL)
ERRORMSGFATAL(safe_snprintf("Could not load line %d of config file: %s", line, config_errorstr(error)));
section = getsectionid(cfiledata, "main");
if (section == -1)
ERRORMSGFATAL("Could not find main section in config file");
loadstrfromconfig(cfiledata, section, "scratchfile",
&scratchfile, SCRATCHFILE);
if (scratchfile[0] != '/')
ERRORMSGFATAL("Scratchfile is not a valid absolute filename");
sharea = shmem_connect(scratchfile);
if (sharea != NULL)
{
if (do_getpid)
pnums_showpid();
else if (count)
pnums_count(username);
else
pnums_list(username, useips);
}
else
{
if (do_getpid)
ERRORMSGFATAL("No parent pid in inetd mode.");
else if (count)
inetd_count(cfiledata, username);
else
inetd_list(username, useips);
}
return(0);
}
syntax highlighted by Code2HTML, v. 0.9.1