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