--- /dev/null
+/*
+ * The Remote User Information Lister 1.8
+ *
+ * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU General Public License. See file COPYING in any of the GNU packages.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <netinet/in.h>
+
+#include "net.h"
+
+static int is_uptime;
+
+static void
+puttime(int s)
+{
+ int d, h, m;
+ if (s < 100)
+ printf("%4ds", s);
+ else
+ {
+ m = s/60;
+ s %= 60;
+ h = m/60;
+ m %= 60;
+ if (h < 100)
+ printf("%02d.%02d", h, m);
+ else
+ {
+ d = h/24;
+ h %= 24;
+ if (d < 100)
+ printf("%2dd%02d", d, h);
+ else
+ printf("%4dd", d);
+ }
+ }
+}
+
+static void
+show_uptime(char *name, struct rywho_pkt *p)
+{
+ int i;
+
+ printf("%-16s up ", name);
+ puttime(ntohl(p->uptime));
+ printf(" load");
+ for(i=0; i<3; i++)
+ {
+ int l = ntohl(p->avl[i]);
+ printf(" %2d.%02d", l/100, l%100);
+ }
+ putchar('\n');
+}
+
+static void
+show_users(char *name, struct rywho_pkt *p)
+{
+ int u;
+ int m = ntohl(p->num_users);
+ struct userinfo *i;
+
+ for(u=0; u<m; u++)
+ {
+ i = &p->users[u];
+ printf("%-8s %-3s %c %-16s ", i->name, i->con, (i->mesg_y ? ' ' : '-'), name);
+ puttime(ntohl(i->login_time));
+ putchar(' ');
+ puttime(ntohl(i->idle_time));
+ putchar('\n');
+ }
+ if (m == MAX_USERS)
+ printf("%s: MAX_USERS reached!\n", name);
+}
+
+static void
+scan(void)
+{
+ DIR *d;
+ struct dirent *e;
+ struct rywho_pkt pkt;
+ int fd, r;
+ int is = 0;
+
+ if (chdir(YWHO_SPOOL_DIR) < 0)
+ {
+ fprintf(stderr, "chdir(" YWHO_SPOOL_DIR "): %m\n");
+ exit(1);
+ }
+ d = opendir(".");
+ if (!d)
+ {
+ perror("opendir");
+ exit(1);
+ }
+ while (e = readdir(d))
+ if (e->d_name[0] != '.')
+ {
+ fd = open(e->d_name, O_RDONLY);
+ if (fd < 0)
+ {
+ fprintf(stderr, "%s: %m\n", e->d_name);
+ continue;
+ }
+ r = read(fd, &pkt, sizeof(pkt));
+ close(fd);
+ if (r <= sizeof(struct rywho_pkt) - MAX_USERS*sizeof(struct userinfo)
+ || r != sizeof(struct rywho_pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo))
+ {
+ fprintf(stderr, "%s: Malformed record\n", e->d_name);
+ continue;
+ }
+ (is_uptime ? show_uptime : show_users)(e->d_name, &pkt);
+ is = 1;
+ }
+ closedir(d);
+ if (!is)
+ puts("No data available.");
+}
+
+int
+main(int argc, char **argv)
+{
+ if (strstr(argv[0], "uptime"))
+ is_uptime = 1;
+ if (argc != 1)
+ {
+ fprintf(stderr, "Usage: %s\n", argv[0]);
+ return 1;
+ }
+ if (!is_uptime)
+ puts("Name Li M Where LogT IdleT");
+ scan();
+ return 0;
+}
--- /dev/null
+/*
+ * The Remote User Information Daemon 1.8
+ *
+ * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU General Public License. See file COPYING in any of the GNU packages.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <utmp.h>
+#include <signal.h>
+#include <dirent.h>
+
+#include "net.h"
+
+struct hostrec {
+ struct hostrec *next;
+ char name[32];
+ time_t last_rec; /* 0=down */
+ __u32 addr;
+};
+
+static int sock, port;
+static struct hostrec *first_host;
+static time_t now, last_local_scan;
+
+static void die(char *msg, ...) __attribute__((noreturn));
+
+static void
+die(char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ fprintf(stderr, "nwhod: ");
+ vfprintf(stderr, msg, args);
+ fputc('\n', stderr);
+ exit(1);
+}
+
+static void
+net_init(char *name)
+{
+ struct hostent *h;
+ struct sockaddr_in sa;
+ struct servent *se;
+ int one;
+
+ if (name)
+ {
+ if (! (h = gethostbyname(name)))
+ die("%s: %m", name);
+ }
+ else
+ h = NULL;
+
+ if (! (se = getservbyname("nwho", "udp")))
+ die("Unknown service `nwho/udp'");
+
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0)
+ die("socket: %m");
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
+ die("setsockopt: %m");
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = port = se->s_port;
+ sa.sin_addr.s_addr = INADDR_ANY;
+ if (bind(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ die("bind: %m");
+
+ if (h)
+ {
+ memcpy(&sa.sin_addr.s_addr, h->h_addr, sizeof(sa.sin_addr.s_addr));
+ if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ die("connect: %m");
+ }
+}
+
+static void
+scan_utmp(struct rywho_pkt *p, time_t now)
+{
+ struct utmp *u;
+ int cnt = 0;
+ struct stat st;
+ char device[32];
+ struct userinfo *h;
+ char name[UT_NAMESIZE+10];
+ time_t last;
+
+ setutent();
+ while ((u = getutent()) && cnt < MAX_USERS)
+ if (u->ut_type == USER_PROCESS && u->ut_user[0])
+ {
+ h = &p->users[cnt];
+ memcpy(name, u->ut_user, UT_NAMESIZE);
+ name[9] = 0;
+ strcpy(h->name, name);
+ h->login_time = htonl(now - u->ut_time);
+ if (u->ut_id[0])
+ {
+ h->con[0] = u->ut_id[0];
+ h->con[1] = u->ut_id[1];
+ h->con[2] = 0;
+ }
+ else
+ {
+ char *z = u->ut_line;
+ if (!strncmp(z, "tty", 3))
+ z += 3;
+ sprintf(h->con, "%.3s", z);
+ }
+ sprintf(device, "/dev/%s", u->ut_line);
+ if (stat(device, &st) < 0)
+ {
+ fprintf(stderr, "stat(%s): %m", device);
+ continue;
+ }
+ h->mesg_y = !!(S_IWGRP & st.st_mode);
+ last = st.st_atime;
+ if (st.st_mtime > last)
+ last = st.st_mtime;
+ if (st.st_ctime > last)
+ last = st.st_ctime;
+ if (now < last)
+ last = now;
+ h->idle_time = htonl(now - last);
+ cnt++;
+ }
+ p->num_users = htonl(cnt);
+}
+
+static void
+scan_load(struct rywho_pkt *p)
+{
+ int n, i, j[6];
+ char buf[256];
+
+ n = open("/proc/uptime", O_RDONLY);
+ if (n < 0)
+ return;
+ if (read(n, buf, sizeof(buf)) <= 0)
+ return;
+ close(n);
+ if (!sscanf(buf, "%d", &i))
+ return;
+ p->uptime = htonl(i);
+
+ n = open("/proc/loadavg", O_RDONLY);
+ if (n < 0)
+ return;
+ if (read(n, buf, sizeof(buf)) <= 0)
+ return;
+ close(n);
+ if (sscanf(buf, "%d.%d%d.%d%d.%d", &j[0], &j[1], &j[2], &j[3], &j[4], &j[5]) != 6)
+ return;
+ p->avl[0] = htonl(j[0]*100 + j[1]);
+ p->avl[1] = htonl(j[2]*100 + j[3]);
+ p->avl[2] = htonl(j[4]*100 + j[5]);
+}
+
+static void
+make_pkt(struct rywho_pkt *pkt)
+{
+ bzero(pkt, sizeof(pkt));
+ pkt->local_time = htonl(now);
+ scan_utmp(pkt, now);
+ scan_load(pkt);
+}
+
+static void
+cleanup(void)
+{
+ DIR *d;
+ struct dirent *e;
+
+ d = opendir(".");
+ if (!d)
+ die("opendir: %m");
+ while (e = readdir(d))
+ if (e->d_name[0] != '.')
+ unlink(e->d_name);
+ closedir(d);
+}
+
+static void
+save_pkt(char *name, struct rywho_pkt *pkt, int len)
+{
+ int fd = open(name, O_WRONLY | O_CREAT, 0666);
+ if (fd < 0)
+ {
+ syslog(LOG_ERR, "open(%s): %m", name);
+ return;
+ }
+ pkt->server_time = htonl(now);
+ if (write(fd, pkt, len) != len)
+ syslog(LOG_ERR, "write: %m");
+ ftruncate(fd, len);
+ close(fd);
+}
+
+static inline int /* Validation checks not implemented yet */
+is_valid(__u32 ipa)
+{
+ return 1;
+}
+
+static void
+receive(void)
+{
+ int r, al;
+ struct rywho_pkt pkt;
+ struct sockaddr_in sa;
+ int n = sizeof(struct rywho_pkt) - MAX_USERS * sizeof(struct userinfo);
+ struct hostrec *e;
+ struct hostent *h;
+ char *c;
+
+ alarm(DEFAULT_PRUNE_TIME);
+ al = sizeof(sa);
+ r = recvfrom(sock, &pkt, sizeof(pkt), 0, (struct sockaddr *) &sa, &al);
+ alarm(0);
+ now = time(NULL);
+ if (r < 0)
+ {
+ if (errno == EINTR)
+ return;
+ syslog(LOG_ERR, "recvfrom: %m");
+ sleep(5);
+ return;
+ }
+
+ if (!is_valid(sa.sin_addr.s_addr) || sa.sin_port != port)
+ {
+ syslog(LOG_WARNING, "Received packet from invalid source %s.%d", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
+ return;
+ }
+
+ if (r < n || r != n + ntohl(pkt.num_users)*sizeof(struct userinfo))
+ {
+ syslog(LOG_WARNING, "Malformed packet from %s", inet_ntoa(sa.sin_addr));
+ return;
+ }
+
+ for(e=first_host; e; e=e->next)
+ if (e->addr == sa.sin_addr.s_addr)
+ break;
+ if (!e)
+ {
+ e = malloc(sizeof(struct hostrec));
+ if (!e)
+ {
+ syslog(LOG_ERR, "Out of memory");
+ return;
+ }
+ e->next = first_host;
+ first_host = e;
+ h = gethostbyaddr((char *) &sa.sin_addr, sizeof(sa.sin_addr), AF_INET);
+ if (h)
+ {
+ sprintf(e->name, "%.30s", h->h_name);
+ for(c=e->name; *c; c++)
+ if (*c == '.')
+ {
+ *c = 0;
+ break;
+ }
+ else if ((*c < 'A' || *c > 'Z') /* Filter out malicious characters */
+ && (*c < 'a' || *c > 'z')
+ && (*c < '0' || *c > '9')
+ && *c != '-')
+ *c = '_';
+ }
+ else
+ strcpy(e->name, inet_ntoa(sa.sin_addr));
+ e->addr = sa.sin_addr.s_addr;
+ }
+
+ e->last_rec = now;
+ save_pkt(e->name, &pkt, r);
+}
+
+static void
+local_scan(void)
+{
+ struct rywho_pkt pkt;
+ static char hostname[64];
+
+ make_pkt(&pkt);
+ if (!hostname[0] && gethostname(hostname, sizeof(hostname)) < 0)
+ die("gethostname: %m");
+ save_pkt(hostname, &pkt, sizeof(pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo));
+}
+
+static void
+do_tick(void)
+{
+ struct hostrec *e;
+
+ if (last_local_scan + DEFAULT_SEND_TIME <= now)
+ {
+ last_local_scan = now;
+ local_scan();
+ }
+ for(e=first_host; e; e=e->next)
+ if (e->last_rec && e->last_rec + DEFAULT_DEAD_TIME < now)
+ {
+ if (unlink(e->name) < 0)
+ syslog(LOG_ERR, "unlink(%s): %m", e->name);
+ e->last_rec = 0;
+ }
+}
+
+static void
+tick(int unused)
+{
+}
+
+static struct sigaction sigact = { tick, 0, 0, NULL };
+
+static void
+server(void)
+{
+ net_init(NULL);
+ if (chdir(YWHO_SPOOL_DIR) < 0)
+ die("chdir(" YWHO_SPOOL_DIR "): %m");
+ cleanup();
+ sigaction(SIGALRM, &sigact, NULL);
+ now = time(NULL);
+ for(;;)
+ {
+ do_tick();
+ receive();
+ }
+}
+
+static void
+client(char *serv)
+{
+ struct rywho_pkt pkt;
+
+ net_init(serv);
+ utmpname(UTMP_FILE);
+ for(;;)
+ {
+ now = time(NULL);
+ make_pkt(&pkt);
+ if (send(sock, &pkt, sizeof(pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo), 0) < 0)
+ syslog(LOG_ERR, "sendmsg: %m");
+ sleep(DEFAULT_SEND_TIME);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int pid;
+
+ if (argc != 1 && argc != 2)
+ {
+ fprintf(stderr, "Usage: nwhod [<server>]\n");
+ return 1;
+ }
+ pid = fork();
+ if (pid < 0)
+ {
+ perror("fork");
+ return 1;
+ }
+ if (pid)
+ return 0;
+ if (argc == 2)
+ client(argv[1]);
+ else if (argc == 1)
+ server();
+ return 1;
+}