#include <stdio.h>
#include <signal.h>
#include <sys/stat.h>
#include <rpc/rpc.h>
#include <memory.h>
#include <netconfig.h>
#include <stropts.h>
#include <syslog.h>
#include <utmpx.h>
#include <rpcsvc/rusers.h>
#include <sys/resource.h>
#include <limits.h>
#ifdef DEBUG
#define RPC_SVC_FG
#endif
#define _RPCSVC_CLOSEDOWN 120
static void rusers_service();
static void closedown();
static void msgout();
static unsigned min();
static int _rpcpmstart;
static int _rpcfdtype;
static int _rpcsvcdirty;
static int _rpcsvcrecent;
#define DIV60(t) ((t+30)/60)
#define ALL_ENTRIES 1
#define REAL_USERS 0
utmp_array utmp_array_res;
int used_array_len = 0;
struct utmpidlearr utmpidlearr;
static void free_ua_entry(rusers_utmp *uap);
static int findidle(char *name, int ln, time_t now);
static void usys5to_ru(struct utmpx *s5, struct ru_utmp *bss);
int
main(int argc, char *argv[])
{
pid_t pid;
int i;
int connmaxrec = RPC_MAXDATASIZE;
if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) {
msgout("unable to set maximum RPC record size");
}
if (t_getstate(0) != -1 || t_errno != TBADF) {
char *netid;
struct netconfig *nconf = NULL;
SVCXPRT *transp;
int pmclose;
extern char *getenv();
_rpcpmstart = 1;
openlog("rusers", LOG_PID, LOG_DAEMON);
if ((netid = getenv("NLSPROVIDER")) == NULL) {
#ifdef DEBUG
msgout("cannot get transport name");
#endif
} else if ((nconf = getnetconfigent(netid)) == NULL) {
#ifdef DEBUG
msgout("cannot get transport info");
#endif
}
if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
msgout("cannot create server handle");
exit(1);
}
if (nconf)
freenetconfigent(nconf);
if (!svc_reg(transp, RUSERSPROG, RUSERSVERS_3, rusers_service,
0)) {
msgout("unable to register (RUSERSPROG, RUSERSVERS_3).");
exit(1);
}
if (!svc_reg(transp, RUSERSPROG, RUSERSVERS_IDLE,
rusers_service, 0)) {
msgout("unable to register (RUSERSPROG, RUSERSVERS_IDLE).");
exit(1);
}
(void) signal(SIGALRM, closedown);
(void) alarm(_RPCSVC_CLOSEDOWN);
svc_run();
msgout("svc_run returned");
exit(1);
}
#ifndef RPC_SVC_FG
pid = fork();
if (pid < 0) {
perror("rpc.rusersd: cannot fork");
exit(1);
}
if (pid)
exit(0);
for (i = 0; i < 20; i++)
(void) close(i);
setsid();
openlog("rusers", LOG_PID, LOG_DAEMON);
#endif
if (!svc_create(rusers_service, RUSERSPROG, RUSERSVERS_3, "netpath")) {
msgout("unable to create (RUSERSPROG, RUSERSVERS_3) for netpath");
exit(1);
}
if (!svc_create(rusers_service, RUSERSPROG, RUSERSVERS_IDLE,
"netpath")) {
msgout(
"unable to create (RUSERSPROG, RUSERSVERS_IDLE) for netpath");
exit(1);
}
svc_run();
msgout("svc_run returned");
return (1);
}
int
getutmpx_3(all, version, limit)
int all;
int version;
int limit;
{
struct utmpx *utent;
struct utmpidle **q = utmpidlearr.uia_arr;
int minidle;
int cnt = 0;
time_t now;
extern char *s_malodup();
time(&now);
setutxent();
while ((utent = getutxent()) != NULL && (limit == 0 || cnt < limit)) {
if (utent->ut_line[0] == '\0' || utent->ut_user[0] == '\0')
continue;
if ((all == REAL_USERS) && ((utent->ut_type != USER_PROCESS) ||
nonuserx(*utent)))
continue;
if (version == RUSERSVERS_IDLE) {
*q = (struct utmpidle *)
malloc(sizeof (struct utmpidle));
(*q)->ui_idle = findidle(utent->ut_line,
sizeof (utent->ut_line), now);
if (strncmp(utent->ut_line, "console",
strlen("console")) == 0) {
(*q)->ui_idle = min((*q)->ui_idle,
console_idle(now));
}
usys5to_ru(utent, &((*q)->ui_utmp));
#ifdef DEBUG
printf("%-*s %-*s %s; idle %d",
sizeof (utent->ut_line),
utent->ut_line,
sizeof (utent->ut_name),
utent->ut_name,
ctime(&utent->ut_xtime),
(*q)->ui_idle);
#endif
q++;
} else if (version == RUSERSVERS_3) {
#define uav utmp_array_res.utmp_array_val
uav[cnt].ut_host =
s_malodup(utent->ut_host, utent->ut_syslen);
uav[cnt].ut_user = s_malodup(utent->ut_user,
sizeof (utent->ut_user));
uav[cnt].ut_line = s_malodup(utent->ut_line,
sizeof (utent->ut_line));
uav[cnt].ut_type = utent->ut_type;
uav[cnt].ut_time = utent->ut_xtime;
uav[cnt].ut_idle = findidle(utent->ut_line,
sizeof (utent->ut_line), now);
if (strncmp(utent->ut_line, "console",
strlen("console")) == 0) {
uav[cnt].ut_idle =
min(uav[cnt].ut_idle,
console_idle(now));
}
#ifdef DEBUG
printf("user: %-10s line: %-10s %s; idle %d (%s)\n",
uav[cnt].ut_line, uav[cnt].ut_user,
ctime((time_t *)&uav[cnt].ut_time),
uav[cnt].ut_idle, uav[cnt].ut_host);
#endif
#undef uav
}
cnt++;
}
return (cnt);
}
char *
s_malodup(string, size)
char *string;
int size;
{
char *tmp;
tmp = (char *)malloc(size+1);
if (tmp == NULL) {
msgout("rpc.rusersd: malloc failed (2)");
return (NULL);
}
strncpy(tmp, string, size);
tmp[size] = '\0';
return (tmp);
}
int
console_idle(now)
time_t now;
{
return (min((unsigned)findidle("kbd", strlen("kbd"), now),
(unsigned)findidle("mouse", strlen("mouse"), now)));
}
static void
rusers_service(rqstp, transp)
register struct svc_req *rqstp;
register SVCXPRT *transp;
{
int i;
int cnt;
char *replyerr = "rpc.rusersd: error replying to request";
_rpcsvcrecent = _rpcsvcdirty = 1;
switch (rqstp->rq_proc) {
case 0:
if (svc_sendreply(transp, xdr_void, 0) == FALSE) {
msgout(replyerr);
}
break;
case RUSERSPROC_NUM:
cnt = getutmpx_3(REAL_USERS, 0, 0);
if (!svc_sendreply(transp, xdr_u_long, (caddr_t)&cnt))
msgout(replyerr);
break;
case RUSERSPROC_NAMES:
case RUSERSPROC_ALLNAMES:
if (rqstp->rq_vers == RUSERSVERS_IDLE) {
utmpidlearr.uia_arr = (struct utmpidle **)
malloc(MAXUSERS*sizeof (struct utmpidle *));
utmpidlearr.uia_cnt = getutmpx_3(rqstp->rq_proc ==
RUSERSPROC_ALLNAMES,
RUSERSVERS_IDLE, MAXUSERS);
if (!svc_sendreply(transp, xdr_utmpidlearr,
(caddr_t)&utmpidlearr))
msgout(replyerr);
for (i = 0; i < utmpidlearr.uia_cnt; i++) {
free(utmpidlearr.uia_arr[i]);
}
free(utmpidlearr.uia_arr);
} else if (rqstp->rq_vers == RUSERSVERS_3) {
int entries, alloc_array_len;
for (i = 0; i < used_array_len; i++) {
free_ua_entry(&utmp_array_res.utmp_array_val[i]);
}
entries = (rqstp->rq_proc == RUSERSPROC_ALLNAMES);
cnt = getutmpx_3(entries, 0, 0);
if (cnt > utmp_array_res.utmp_array_len) {
free(utmp_array_res.utmp_array_val);
utmp_array_res.utmp_array_len = 0;
utmp_array_res.utmp_array_val = (rusers_utmp *)
malloc(cnt * sizeof (rusers_utmp));
if (utmp_array_res.utmp_array_val == NULL) {
msgout("rpc.rusersd: malloc failed (1)");
break;
}
alloc_array_len = cnt;
} else {
alloc_array_len = utmp_array_res.utmp_array_len;
}
cnt = getutmpx_3(entries, RUSERSVERS_3, cnt);
utmp_array_res.utmp_array_len = used_array_len = cnt;
if (!svc_sendreply(transp, xdr_utmp_array,
(caddr_t)&utmp_array_res))
msgout(replyerr);
utmp_array_res.utmp_array_len = alloc_array_len;
}
break;
default:
svcerr_noproc(transp);
break;
}
_rpcsvcdirty = 0;
}
static void
free_ua_entry(rusers_utmp *uap)
{
if (uap == NULL)
return;
if (uap->ut_user)
free(uap->ut_user);
if (uap->ut_line)
free(uap->ut_line);
if (uap->ut_host)
free(uap->ut_host);
}
static int
findidle(char *name, int ln, time_t now)
{
struct stat stbuf;
long lastaction, diff;
char ttyname[32];
strcpy(ttyname, "/dev/");
strncat(ttyname, name, ln);
if (stat(ttyname, &stbuf) < 0)
return (INT_MAX);
lastaction = stbuf.st_atime;
diff = now - lastaction;
diff = DIV60(diff);
if (diff < 0) diff = 0;
return (diff);
}
static void
usys5to_ru(struct utmpx *s5, struct ru_utmp *bss)
{
int i;
#ifdef DEBUG
printf("sizeof (bss->ut_host) == %d\n", sizeof (bss->ut_host));
#endif
strncpy(bss->ut_name, s5->ut_name, sizeof (bss->ut_name));
strncpy(bss->ut_line, s5->ut_line, sizeof (bss->ut_line));
strncpy(bss->ut_host, s5->ut_host, sizeof (bss->ut_host));
bss->ut_time = s5->ut_xtime;
}
static void
msgout(msg)
char *msg;
{
#ifdef RPC_SVC_FG
if (_rpcpmstart)
syslog(LOG_ERR, msg);
else
(void) fprintf(stderr, "%s\n", msg);
#else
syslog(LOG_ERR, msg);
#endif
}
static void
closedown(sig)
int sig;
{
if (_rpcsvcrecent) {
_rpcsvcrecent = 0;
} else {
if (_rpcsvcdirty == 0) {
int i, openfd;
struct t_info tinfo;
if (t_getinfo(0, &tinfo) || (tinfo.servtype == T_CLTS))
exit(0);
for (i = 0, openfd = 0;
i < svc_max_pollfd && openfd < 2;
i++) {
if (svc_pollfd[i].fd >= 0)
openfd++;
}
if (openfd <= 1)
exit(0);
}
}
(void) signal(SIGALRM, closedown);
(void) alarm(_RPCSVC_CLOSEDOWN);
}
unsigned
min(a, b)
unsigned a;
unsigned b;
{
if (a < b)
return (a);
else
return (b);
}