]> mj.ucw.cz Git - nwho.git/blob - nwhod.c
812759f6b25e150de74f5e767c8ba4830c6a346a
[nwho.git] / nwhod.c
1 /*
2  *      The Remote User Information Daemon 1.9
3  *
4  *      (c) 1997--2001 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU General Public License. See file COPYING in any of the GNU packages.
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdarg.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <sys/socket.h>
17 #include <netdb.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <syslog.h>
21 #include <sys/stat.h>
22 #include <utmp.h>
23 #include <signal.h>
24 #include <dirent.h>
25 #include <asm/types.h>
26 #include <errno.h>
27
28 #include "net.h"
29
30 struct hostrec {
31   struct hostrec *next;
32   char name[32];
33   time_t last_rec;                      /* 0=down */
34   __u32 addr;
35 };
36
37 static int sock, port;
38 static struct hostrec *first_host;
39 static time_t now, last_local_scan;
40
41 static void die(char *msg, ...) __attribute__((noreturn));
42
43 static void
44 die(char *msg, ...)
45 {
46   va_list args;
47
48   va_start(args, msg);
49   fprintf(stderr, "nwhod: ");
50   vfprintf(stderr, msg, args);
51   fputc('\n', stderr);
52   exit(1);
53 }
54
55 static void
56 net_init(char *name)
57 {
58   struct hostent *h;
59   struct sockaddr_in sa;
60   struct servent *se;
61   int one;
62
63   if (name)
64     {
65       if (! (h = gethostbyname(name)))
66         die("%s: %m", name);
67     }
68   else
69     h = NULL;
70
71   if (! (se = getservbyname("nwho", "udp")))
72     die("Unknown service `nwho/udp'");
73
74   sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
75   if (sock < 0)
76     die("socket: %m");
77
78   if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
79     die("setsockopt: %m");
80
81   sa.sin_family = AF_INET;
82   sa.sin_port = port = se->s_port;
83   sa.sin_addr.s_addr = INADDR_ANY;
84   if (bind(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
85     die("bind: %m");
86
87   if (h)
88     {
89       memcpy(&sa.sin_addr.s_addr, h->h_addr, sizeof(sa.sin_addr.s_addr));
90       if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
91         die("connect: %m");
92     }
93 }
94
95 static void
96 scan_utmp(struct rywho_pkt *p, time_t now)
97 {
98   struct utmp *u;
99   int cnt = 0;
100   struct stat st;
101   char device[32];
102   struct userinfo *h;
103   char name[UT_NAMESIZE+10];
104   time_t last;
105
106   setutent();
107   while ((u = getutent()) && cnt < MAX_USERS)
108     if (u->ut_type == USER_PROCESS && u->ut_user[0])
109       {
110         h = &p->users[cnt];
111         memcpy(name, u->ut_user, UT_NAMESIZE);
112         name[9] = 0;
113         strcpy(h->name, name);
114         h->login_time = htonl(now - u->ut_time);
115         sprintf(h->con, "%.7s", u->ut_line);
116         sprintf(device, "/dev/%s", u->ut_line);
117         if (stat(device, &st) < 0)
118           {
119             fprintf(stderr, "stat(%s): %m", device);
120             continue;
121           }
122         h->mesg_y = !!(S_IWGRP & st.st_mode);
123         last = st.st_atime;
124         if (st.st_mtime > last)
125           last = st.st_mtime;
126         if (st.st_ctime > last)
127           last = st.st_ctime;
128         if (now < last)
129           last = now;
130         h->idle_time = htonl(now - last);
131         cnt++;
132       }
133   p->num_users = htonl(cnt);
134 }
135
136 static void
137 scan_load(struct rywho_pkt *p)
138 {
139   int n, i, j[6];
140   char buf[256];
141
142   n = open("/proc/uptime", O_RDONLY);
143   if (n < 0)
144     return;
145   if (read(n, buf, sizeof(buf)) <= 0)
146     return;
147   close(n);
148   if (!sscanf(buf, "%d", &i))
149     return;
150   p->uptime = htonl(i);
151
152   n = open("/proc/loadavg", O_RDONLY);
153   if (n < 0)
154     return;
155   if (read(n, buf, sizeof(buf)) <= 0)
156     return;
157   close(n);
158   if (sscanf(buf, "%d.%d%d.%d%d.%d", &j[0], &j[1], &j[2], &j[3], &j[4], &j[5]) != 6)
159     return;
160   p->avl[0] = htonl(j[0]*100 + j[1]);
161   p->avl[1] = htonl(j[2]*100 + j[3]);
162   p->avl[2] = htonl(j[4]*100 + j[5]);
163 }
164
165 static void
166 make_pkt(struct rywho_pkt *pkt)
167 {
168   bzero(pkt, sizeof(pkt));
169   pkt->magic = htonl(NWHO_MAGIC);
170   pkt->local_time = htonl(now);
171   scan_utmp(pkt, now);
172   scan_load(pkt);
173 }
174
175 static void
176 cleanup(void)
177 {
178   DIR *d;
179   struct dirent *e;
180
181   d = opendir(".");
182   if (!d)
183     die("opendir: %m");
184   while (e = readdir(d))
185     if (e->d_name[0] != '.')
186       unlink(e->d_name);
187   closedir(d);
188 }
189
190 static void
191 save_pkt(char *name, struct rywho_pkt *pkt, int len)
192 {
193   int fd = open(name, O_WRONLY | O_CREAT, 0666);
194   if (fd < 0)
195     {
196       syslog(LOG_ERR, "open(%s): %m", name);
197       return;
198     }
199   pkt->server_time = htonl(now);
200   if (write(fd, pkt, len) != len)
201     syslog(LOG_ERR, "write: %m");
202   ftruncate(fd, len);
203   close(fd);
204 }
205
206 static inline int                       /* Validation checks not implemented yet */
207 is_valid(__u32 ipa)
208 {
209   return 1;
210 }
211
212 static void
213 receive(void)
214 {
215   int r, al;
216   struct rywho_pkt pkt;
217   struct sockaddr_in sa;
218   int n = sizeof(struct rywho_pkt) - MAX_USERS * sizeof(struct userinfo);
219   struct hostrec *e;
220   struct hostent *h;
221   char *c;
222
223   alarm(DEFAULT_PRUNE_TIME);
224   al = sizeof(sa);
225   r = recvfrom(sock, &pkt, sizeof(pkt), 0, (struct sockaddr *) &sa, &al);
226   alarm(0);
227   now = time(NULL);
228   if (r < 0)
229     {
230       if (errno == EINTR)
231         return;
232       syslog(LOG_ERR, "recvfrom: %m");
233       sleep(5);
234       return;
235     }
236
237   if (!is_valid(sa.sin_addr.s_addr) || sa.sin_port != port)
238     {
239       syslog(LOG_WARNING, "Received packet from invalid source %s.%d", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
240       return;
241     }
242
243   if (r < n || r != n + ntohl(pkt.num_users)*sizeof(struct userinfo))
244     {
245       syslog(LOG_WARNING, "Malformed packet from %s", inet_ntoa(sa.sin_addr));
246       return;
247     }
248
249   if (pkt.magic != htonl(NWHO_MAGIC))
250     {
251       syslog(LOG_WARNING, "Received ancient nwho packet from %s", inet_ntoa(sa.sin_addr));
252       return;
253     }
254
255   for(e=first_host; e; e=e->next)
256     if (e->addr == sa.sin_addr.s_addr)
257       break;
258   if (!e)
259     {
260       e = malloc(sizeof(struct hostrec));
261       if (!e)
262         {
263           syslog(LOG_ERR, "Out of memory");
264           return;
265         }
266       e->next = first_host;
267       first_host = e;
268       h = gethostbyaddr((char *) &sa.sin_addr, sizeof(sa.sin_addr), AF_INET);
269       if (h)
270         {
271           sprintf(e->name, "%.30s", h->h_name);
272           for(c=e->name; *c; c++)
273             if (*c == '.')
274               {
275                 *c = 0;
276                 break;
277               }
278             else if ((*c < 'A' || *c > 'Z') /* Filter out malicious characters */
279                      && (*c < 'a' || *c > 'z')
280                      && (*c < '0' || *c > '9')
281                      && *c != '-')
282               *c = '_';
283         }
284       else
285         strcpy(e->name, inet_ntoa(sa.sin_addr));
286       e->addr = sa.sin_addr.s_addr;
287     }
288
289   e->last_rec = now;
290   save_pkt(e->name, &pkt, r);
291 }
292
293 static void
294 local_scan(void)
295 {
296   struct rywho_pkt pkt;
297   static char hostname[64];
298
299   make_pkt(&pkt);
300   if (!hostname[0] && gethostname(hostname, sizeof(hostname)) < 0)
301     die("gethostname: %m");
302   save_pkt(hostname, &pkt, sizeof(pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo));
303 }
304
305 static void
306 do_tick(void)
307 {
308   struct hostrec *e;
309
310   if (last_local_scan + DEFAULT_SEND_TIME <= now)
311     {
312       last_local_scan = now;
313       local_scan();
314     }
315   for(e=first_host; e; e=e->next)
316     if (e->last_rec && e->last_rec + DEFAULT_DEAD_TIME < now)
317       {
318         if (unlink(e->name) < 0)
319           syslog(LOG_ERR, "unlink(%s): %m", e->name);
320         e->last_rec = 0;
321       }
322 }
323
324 static void
325 tick(int unused)
326 {
327 }
328
329 static void
330 server(void)
331 {
332   static struct sigaction sigact;
333
334   net_init(NULL);
335   if (chdir(NWHO_SPOOL_DIR) < 0)
336     die("chdir(" NWHO_SPOOL_DIR "): %m");
337   cleanup();
338   bzero(&sigact, sizeof(sigact));
339   sigact.sa_handler = tick;
340   sigaction(SIGALRM, &sigact, NULL);
341   now = time(NULL);
342   for(;;)
343     {
344       do_tick();
345       receive();
346     }
347 }
348
349 static void
350 client(char *serv)
351 {
352   struct rywho_pkt pkt;
353
354   net_init(serv);
355   utmpname(UTMP_FILE);
356   for(;;)
357     {
358       now = time(NULL);
359       make_pkt(&pkt);
360       if (send(sock, &pkt, sizeof(pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo), 0) < 0)
361         syslog(LOG_ERR, "sendmsg: %m");
362       sleep(DEFAULT_SEND_TIME);
363     }
364 }
365
366 int
367 main(int argc, char **argv)
368 {
369   int pid;
370
371   if (argc != 1 && argc != 2)
372     {
373       fprintf(stderr, "Usage: nwhod [<server>]\n");
374       return 1;
375     }
376   pid = fork();
377   if (pid < 0)
378     {
379       perror("fork");
380       return 1;
381     }
382   if (pid)
383     return 0;
384   if (argc == 2)
385     client(argv[1]);
386   else if (argc == 1)
387     server();
388   return 1;
389 }