]> mj.ucw.cz Git - nwho.git/commitdiff
Introduced the nwho service.
authorMartin Mares <mj@ucw.cz>
Sat, 27 Sep 1997 11:34:41 +0000 (11:34 +0000)
committerMartin Mares <mj@ucw.cz>
Sat, 27 Sep 1997 11:34:41 +0000 (11:34 +0000)
ChangeLog [new file with mode: 0644]
Makefile
net.h [new file with mode: 0644]
nwho.c [new file with mode: 0644]
nwhod.c [new file with mode: 0644]

diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..524abb0
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,15 @@
+Fri Sep 26 23:45:57 1997  Martin Mares  <mj@albireo.mj.gts.cz>
+
+       * 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.
index 3d997d0b9731ae7c128e438d141a89a4666f9d65..1cffb0e46cf89b4fe3ee4e74a49f3198c22fe4e9 100644 (file)
--- 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 (file)
index 0000000..ad733a5
--- /dev/null
+++ b/net.h
@@ -0,0 +1,34 @@
+/*
+ *     The Remote User Info Distribution Protocol
+ *
+ *     (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.
+ */
+
+#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 (file)
index 0000000..f9e1639
--- /dev/null
+++ b/nwho.c
@@ -0,0 +1,143 @@
+/*
+ *     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;
+}
diff --git a/nwhod.c b/nwhod.c
new file mode 100644 (file)
index 0000000..e0f158e
--- /dev/null
+++ b/nwhod.c
@@ -0,0 +1,390 @@
+/*
+ *     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;
+}