From: Martin Mares Date: Sat, 27 Sep 1997 11:34:41 +0000 (+0000) Subject: Introduced the nwho service. X-Git-Tag: v1.13~48 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=170ca16db7d45fe3f146508ff1ab804c149cca1e;p=nwho.git Introduced the nwho service. --- diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..524abb0 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,15 @@ +Fri Sep 26 23:45:57 1997 Martin Mares + + * Added nwhod, nwho and nuptime. Released as version 1.8. + +Ancient history: + + * 1.3 (16-09-96) - idle derived from atime instead of mtime. + * - displaying of current command fixed. + * 1.4 (09-05-97) - "mesg n" state now based on group write + * 1.5 (13-07-97) - correctly parses 2.1.X /proc/meminfo + * - source re-indented + * - fixed zombie detector + * - changed rules for kilo/mega/gigabyte printing + * 1.6 (29-07-97) - ignores unprintable characters in process cmdlines. + * 1.7 (01-09-97) - added missing newline in zombie detector. diff --git a/Makefile b/Makefile index 3d997d0..1cffb0e 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,28 @@ -# Makefile for YWHO +# Makefile for the ywho toolkit -ywho: ywho.c - gcc -s -N -O2 -m486 -fomit-frame-pointer -Wall -Wno-parentheses ywho.c -o ywho +CFLAGS=-O2 -m486 -fomit-frame-pointer -Wall -Wno-parentheses -malign-loops=0 -malign-jumps=0 -malign-functions=2 +LDFLAGS=-s + +all: ywho nwhod nwho + +ywho: ywho.o + +ywho.o: ywho.c + +nwhod: nwhod.o + +nwhod.o: nwhod.c net.h + +nwho: nwho.o + +nwho.o: nwho.c net.h clean: - rm *.o ywho + rm -f *.o ywho nwhod nwho *~ install: strip ywho - mv ywho /usr/local/bin + install -s -m 755 ywho nwho /usr/bin + ln -sf nwho /usr/bin/nuptime + install -s -m 755 nwhod /usr/sbin + install -d -m 755 /var/spool/nwho diff --git a/net.h b/net.h new file mode 100644 index 0000000..ad733a5 --- /dev/null +++ b/net.h @@ -0,0 +1,34 @@ +/* + * The Remote User Info Distribution Protocol + * + * (c) 1997 Martin Mares + * + * 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. + */ + +#define MAX_USERS 128 + +struct userinfo { + char name[10]; + char con[4]; + char mesg_y; + char pad; + __u32 login_time; + __u32 idle_time; +}; + +struct rywho_pkt { + __u32 local_time; + __u32 server_time; /* Reserved for use by the server */ + __u32 num_users; + __u32 uptime; + __u32 avl[3]; + struct userinfo users[MAX_USERS]; +}; + +#define YWHO_SPOOL_DIR "/var/spool/nwho" + +#define DEFAULT_SEND_TIME 30 +#define DEFAULT_PRUNE_TIME 30 +#define DEFAULT_DEAD_TIME 120 diff --git a/nwho.c b/nwho.c new file mode 100644 index 0000000..f9e1639 --- /dev/null +++ b/nwho.c @@ -0,0 +1,143 @@ +/* + * The Remote User Information Lister 1.8 + * + * (c) 1997 Martin Mares + * + * 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 +#include +#include +#include +#include +#include +#include + +#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; uusers[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; +} diff --git a/nwhod.c b/nwhod.c new file mode 100644 index 0000000..e0f158e --- /dev/null +++ b/nwhod.c @@ -0,0 +1,390 @@ +/* + * The Remote User Information Daemon 1.8 + * + * (c) 1997 Martin Mares + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 []\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; +}